home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #2
/
Monster Media No. 2 (Monster Media)(1994).ISO
/
modem
/
ic201c.zip
/
SCRTUTOR.DOC
< prev
next >
Wrap
Text File
|
1994-06-06
|
241KB
|
4,805 lines
Intellicomm (TM)
v2.01
Copyright (C) 1991-1994 Liberation Enterprises. All rights reserved.
---------------------------------------------------------------------
INTELLICOMM SCRIPT LANGUAGE TUTORIAL
---------------------------------------------------------------------
TABLE OF CONTENTS
1. INTRODUCTION . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 What are Scripts? . . . . . . . . . . . . . . . . . . . . . 1
1.2 Why Should I Bother With Scripts? . . . . . . . . . . . . . 1
1.3 Is This Going to Take HOURS? . . . . . . . . . . . . . . . . 2
1.4 Script Learn Mode . . . . . . . . . . . . . . . . . . . . . 2
1.5 How Do I Create a Script? . . . . . . . . . . . . . . . . . 3
1.6 Icom's Internal Editor vs. an External Editor . . . . . . . 4
1.7 Stopping Scripts . . . . . . . . . . . . . . . . . . . . . . 4
1.8 Creating and Running Your First Script . . . . . . . . . . . 5
1.9 A More Exciting Example . . . . . . . . . . . . . . . . . . 6
1.10 Other Ways of Running Scripts . . . . . . . . . . . . . . . 8
1.10.1 Running Script from BIFs (8); 1.10.2 Running
Scripts from Jobs (8); 1.10.3 Running Scripts from DOS
(9); 1.10.4 Running Scripts from Scripts (9); 1.10.5
Running Scripts from the Script Manager (9); 1.10.6
Running Scripts via Function Keys (10)
1.11 It Can't be That Simple, Can It? . . . . . . . . . . . . 10
1.12 Variable Overview . . . . . . . . . . . . . . . . . . . . 15
1.13 A Word on Formatting . . . . . . . . . . . . . . . . . . 18
1.14 Why the Double Quotes ""? . . . . . . . . . . . . . . . . 19
1.15 Specifying Control Characters . . . . . . . . . . . . . . 19
1.16 Tildes . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.17 Specifying Numbers . . . . . . . . . . . . . . . . . . . 20
1.18 Numeric Limits . . . . . . . . . . . . . . . . . . . . . 21
1.19 Compound Statements . . . . . . . . . . . . . . . . . . . 21
1.20 Where To Go From Here . . . . . . . . . . . . . . . . . . 23
1.21 The Secret to Success . . . . . . . . . . . . . . . . . . 24
2. WHAT HAPPENS WHEN A SCRIPT ENDS? . . . . . . . . . . . . . . . 26
2.1 EXIT and RETURN Error codes . . . . . . . . . . . . . . . 27
2.2 Using the HANGUP Command for Error-Recovery . . . . . . . 28
3. INTRODUCTION TO SUBROUTINES (GOSUB) . . . . . . . . . . . . . . 28
3.1 When to use a Subroutine . . . . . . . . . . . . . . . . . 28
3.2 Writing a Subroutine . . . . . . . . . . . . . . . . . . . 29
3.3 GOSUB In Detail . . . . . . . . . . . . . . . . . . . . . 30
3.4 Creating a Subroutine Template . . . . . . . . . . . . . . 31
4. SCRIPT VARIABLES IN DETAIL . . . . . . . . . . . . . . . . . . 32
4.1 Why Would I Want to Use Variables? . . . . . . . . . . . . 32
4.2 Where you CAN'T use Variables . . . . . . . . . . . . . . 32
4.3 User-Defined Variables . . . . . . . . . . . . . . . . . . 32
4.4 User-Defined Variable Rules . . . . . . . . . . . . . . . 33
4.5 Why All the Rules? . . . . . . . . . . . . . . . . . . . . 35
4.6 Using Variables . . . . . . . . . . . . . . . . . . . . . 36
4.7 The Life of a Variable . . . . . . . . . . . . . . . . . . 37
4.8 Global Variables . . . . . . . . . . . . . . . . . . . . . 38
Intellicomm v2.01 SCRTUTOR.DOC ii
4.9 Using Global Variables . . . . . . . . . . . . . . . . . . 40
4.10 Passing Parameters to Scripts . . . . . . . . . . . . . . 41
4.11 Checking the Number of Passed Parameters . . . . . . . . 43
4.12 System Variables . . . . . . . . . . . . . . . . . . . . 44
4.13 BIF Variables . . . . . . . . . . . . . . . . . . . . . . 45
4.14 Main Setup and Environment Variables . . . . . . . . . . 45
4.15 Environment Variables . . . . . . . . . . . . . . . . . . 45
5. PERMANENT VARIABLES: INTRODUCTION TO FILE INPUT/OUTPUT . . . . 46
5.1 FOPEN 'Modes' . . . . . . . . . . . . . . . . . . . . . . 47
5.2 The File Handle . . . . . . . . . . . . . . . . . . . . . 48
5.3 Testing for End-of-File . . . . . . . . . . . . . . . . 53
5.4 How to store settings on-disk . . . . . . . . . . . . . . 53
5.5 Moving the File Pointer (Advanced Use Only) . . . . . . . 54
5.6 Opening a File for Reading AND Writing (Advanced use only) 57
5.7 Upating an Old Data File . . . . . . . . . . . . . . . . . 59
6. INTRODUCTION TO DATABASE COMMANDS (FILE TAGGER CATALOGS) . . . 62
6.1 Why would I want to use the catalog-oriented commands? . . 62
6.2 What is a Database? . . . . . . . . . . . . . . . . . . . 62
6.3 Indexes . . . . . . . . . . . . . . . . . . . . . . . . . 63
6.4 Using File Tagger Indexes . . . . . . . . . . . . . . . . 64
6.5 How this all applies to Scripts . . . . . . . . . . . . . 65
6.6 The View Date . . . . . . . . . . . . . . . . . . . . . . 70
6.7 Getting Around in a Catalog . . . . . . . . . . . . . . . 71
6.8 Getting the Total Number of Records . . . . . . . . . . . 72
6.9 The View Date and Tagged/Noted Files . . . . . . . . . . . 73
7. USING THE SCRIPT DEBUGGER . . . . . . . . . . . . . . . . . . . 74
7.1 What are BUGS, and What is a DEBUGGER? . . . . . . . . . . 74
7.2 Using the Debugger . . . . . . . . . . . . . . . . . . . . 75
7.3 Checking the Contents of a Single Variable . . . . . . . . 77
7.4 Debug Hotkeys . . . . . . . . . . . . . . . . . . . . . . 78
Intellicomm v2.01 SCRTUTOR.DOC 1
1. INTRODUCTION
1.1 What are Scripts?
Scripts are files which contain one or more instructions (commands) for
Intellicomm to carry out. You can't 'talk' to Intellicomm to tell it
what to do when you have custom work to do (at least, not to my
knowledge... <grin>), so the next best thing is to write it down and let
Intellicomm read it. As a movie script tells the cast what to say and
do: Intellicomm scripts tell Intellicomm what to say and do. You're the
script writer and Intellicomm is your cast.
Don't confuse scripts with Icom's automated jobs and BIFs. The jobs you
run from the Job Directory and set up in the Job Editor are not scripts;
they're called Jobs and they are carried out by ICOM.EXE's built-in
automated routines, using BBS Information File (BIF) data. Jobs are
jobs, BIFs are BIFs, and scripts are scripts. They're three different
things, and this document discusses scripts only. For information on
Icom's internal jobs (automated file uploads and downloads, mail
transfers and time bank transactions, etc.) please refer to the online
help, and particularly to "BIF Learn".
1.2 Why Should I Bother With Scripts?
There are limitless benefits awaiting those who learn some (or all) of
Intellicomm's script language, and learn the simple process of creating
scripts. Most importantly, you will gain an incredibly flexible and
powerful tool to add to your automation arsenal. Virtually ANYTHING you
can dream up can be automated with a script, while BIFs and automated
jobs (aside from the job "Custom Commands" which allow limited custom
work) were designed for specific tasks.
With scripts you can automate *any* BBS task by watching for specific
text from the BBS and handling it as you see fit (send responses to the
BBS, transfer files, display information on the screen, and do any number
of other things). If desired, you can easily define your own custom
interfaces using menus and other interactive functions for interactive
user input, you can display multiple boxes/windows and other text on the
screen (including the ability to display incoming text from the *BBS* in
a window) using a bevy of powerful video-oriented script commands, you
can run other DOS programs (or .BAT files, or other Icom scripts), pause
script execution until a specific time and/or day, create, display, and
otherwise maintain File Tagger catalogs in every way imagineable, read
from and write to text files on-disk (save information permanently),
display text files in the File Viewer or load them into the Editor for
the user to view/modify, test for certain system conditions (date, time,
day-of-week, etc.), get and set BIF and Icom main setup information,
create 'keyboard macros', and on and on.
Those who gain control over both Icom's built-in jobs AND its script
language (and script learn mode, for registered users) truly have the
most powerful and flexible set of tools available for automated BBS
communications, probably anywhere in the universe. If automation, and
Intellicomm v2.01 SCRTUTOR.DOC 2
taking the drudgery out of your online sessions is your passion, you
can't beat that prospect!
1.3 Is This Going to Take HOURS?
Don't be discouraged by the size of the script manuals. The script
language and manuals are like a big bowl of peanuts: take what you want.
You don't have to eat the whole bowl to enjoy yourself and get something
useful done, and you'll be able to write very useful scripts after
reading just the next few pages below. If you just want the basics, and
the basics are VERY powerful and useful indeed, you should be finished in
half-an-hour or less. If you end up drooling at the possibilities and
want the whole bowl of peanuts, it'll take some practice (and some more
reading) to get the whole language down. But it's your choice, and
making progress and getting things DONE is a quick process as you'll soon
see.
You don't even have to print this document if you don't have the time or
paper: Just read it in the help system ("SCRTUTOR.DOC" link; make sure
SCRTUTOR.DOC is in the same directory as ICOM.EXE) or text viewer (via
the Icom File Manager / "View" option) until you're satisfied with what
you've learned. The script documents should be used as follows:
1. Read the introductory material in this document until you're happy
with what you've learned. This document gives you the basics and,
more importantly, shows you how to put script commands together to
get something practical done.
2. Once you've got the basics down, use the "SCRIPT COMMANDS AT A
GLANCE" section at the beginning of SCRIPT.DOC to find the command(s)
you're interested in, and to refresh your memory as to what various
commands do.
3. When you find the command you want, using step 2 above, look it up in
the DETAILED COMMAND SUMMARY (sorted alphabetically by script
command) in SCRIPT.DOC to quickly get all the details along with an
EXAMPLE showing how to use the command.
TIP: If you view SCRIPT.DOC from Icom's internal File Viewer or help
system, to locate a command summary press [Alt-F] (Find) then type the
command you're interested in, follow that with an underscore (e.g.
PRINT_) then carry out the search DOWNWARDS to find the Detailed Summary
in a flash. All commands in the Detailed Command Summary of SCRIPT.DOC
are followed by a line of underscores to allow you to find commands
quickly. The Table of Contents, and the index at the back of the
manuals can also be used to locate information quickly. Many people
skip the Table of Contents, but you can learn a lot by looking at it
carefully. You'll see how the manuals are laid out, and more
importantly can quickly see every major topic that is covered -- which
can be useful to know when you have a question later.
1.4 Script Learn Mode
The easiest way to become acquainted with scripts is to use Icom's script
Intellicomm v2.01 SCRTUTOR.DOC 3
learn mode (a bonus feature in the registered version). With script
learn mode you simply turn it on, then just 'do' whatever it is you want
automated. Icom watches what you do and writes a script for you! Not
only do you save time getting your scripts written, but it's also quite
educational: by looking at the script Icom creates, you can learn how the
script language works.
Script Learn is accessed through the "Learn Modes" option of the Main
Menu or Job Directory or Terminal "Tools" menu, and in various other
places. Pressing [Alt-Q] from just about anywhere in Intellicomm calls
up the Learn Modes menu.
Use of Script Learn is quite straightforward, and is covered in the
online help if you have questions.
1.5 How Do I Create a Script?
Writing a script (or modifying/adding to one started with Script Learn)
is much the same as typing a letter in your word processor. Instead of
using a word processor though, which places printer commands and
formatting codes all over the document, you instead use a "Text Editor"
to create scripts.
Text Editors work similarly to word processors as far as typing,
deleting, cutting/pasting text, etc. But they don't add printer codes to
the files you save to disk -- and they usually don't force margins on
you, which is essential for writing scripts. Script command lines will
sometimes be longer than your screen width, and thus the margins in word
processors which 'wrap' long lines are not acceptable. The files Text
Editors produce are called "text files", or "ASCII files" and these are
the types of files Icom requires for scripts -- without any printer
formatting codes or margins.
Intellicomm comes with a built-in Text Editor, though you can use your
favorite external Text Editor if you like (details in a second). For
script writing, the editor (whether internal or external) is best
accessed through the Icom "Script Manager". By accessing the Script
Manager first you can be sure that your scripts will be saved in the
proper directory; since Icom changes DOS to the Script Directory
(\ICOM\SCR by default) when you enter the Script Manager. From the
editor you just save your scripts in the current directory (without
specifying a drive letter or \DIRECTORY\ name), and they always end up in
the right place, where Icom expects them to be.
The Script Manager can be accessed from the Icom Main Menu (and at
various other menus), or by simply pressing [Alt-U] from just about
anywhere in Intellicomm. [Sorry about the 'U'; it doesn't help much in
remembering the key, but unfortunately [Alt-S] (S for Script) is a fairly
standard 'Screen Capture' command and is used by the Terminal.]
Once in the Script Manager, select "Create" from the bottom menu to
create a new script, or hilight the script of interest and select "Edit"
to view/modify an existing script, or hilight a script and select "Run"
to execute it (the other options in the Script Manager are fairly self-
Intellicomm v2.01 SCRTUTOR.DOC 4
explanatory... please see the online help if you need more information).
There are several scripts included with Intellicomm for demonstration and
other purposes. You can enter the Script Manager, hilight and "Run"
SCRDEMO.SCR from the Script Manager now if you want to see a script in
action before you continue reading. SCRDEMO.SCR can also be viewed after
you run it, for many useful examples. Note that Intellicomm itself
doesn't use SCRDEMO.SCR for anything, so you can delete the file when
you're done with it, if you're short on disk space.
1.6 Icom's Internal Editor vs. an External Editor
If you use Icom's internal Text Editor, you gain the advantage of online
script writing help (online access to the documentation). So, when you
forget one of the script commands we'll be discussing below you can
simply press the [F1] help key, and call up the index of script help.
You can view this document (if installed in the \ICOM directory) or
SCRIPT.DOC to obtain any information you require, without leaving the
editor by accessing the SCRTUTOR.DOC or SCRIPT.DOC help links (available
from the "Script Language" Index, and various other places).
A further advantage of using the internal editor is that if by chance you
make a typing mistake in your script, then Run the script before noticing
the error; Icom will be able to load the script into the internal editor,
position the cursor right on the line of the script where the error
occurred, displaying the error message to you so you can fix it. With an
external editor Icom will still call your editor if an error is found,
and if your editor accepts a filename on the command line the script will
be loaded for you, but you won't be moved to the proper line number. You
can get the error message (which includes the number of the line on which
the error occurred) from the file \ICOM\SCRIPT.ERR while in your external
editor, but unless you're proficient with your external editor and are
used to doing this sort of thing (with other script languages or program
compilers), you're probably better off using Icom's internal editor for
the time being.
To use an external editor you must define the editor command in the Icom
main setup (accessed via the main menu "Intellicomm Setup" option), on
the "Filenames and Paths" screen. Hilight the "Extnl Editor" item, then
press [Enter] and enter the command you'd normally use to start your
editor from the *DOS command prompt* there, and save your setup. Icom
will then call your external editor when you select "Create" or "Edit"
from within the Script Manager or when a script error occurs.
If you defined an external editor previously and wish to reverse your
decision for now and use the internal editor, do the same as outlined
above but CLEAR the "Extnl Editor" item in the main setup (hold down the
[Del] key until the item is clear, or press [Ctrl-End] once to clear the
item while editing it), then re-save your setup. Icom will then use its
internal text editor.
1.7 Stopping Scripts
Before we create and run a script, it'll be useful for you to first know
Intellicomm v2.01 SCRTUTOR.DOC 5
how to STOP one, if something goes wrong. To stop a script, just press
[Alt-Q] (Q for Quit), relax (the script is paused while you're in the
[Alt-Q] menu or any other menu) then select "Abort Script Only" to cancel
the script without cancelling the entire job (if the script was called
during an automated job... more on that later), or select "Abort
Job/Script" to cancel both the script and the job. To activate script
debugging, which runs a script one line at a time, showing you each line
before it is executed select "Script DEBUG" from the [Alt-Q] menu.
Script debug can also be helpful in learning script writing, since it
slows script execution down, allowing you to study each command before it
is executed, and see its result after it is executed.
If you forget the [Alt-Q] command, just press [Alt-Z] to pop up the
Terminal menu (the terminal is where all scripts are executed from,
regardless of where they're started from) and select "Job and Script
Control Menu" from the menu, which has the same effect as pressing [Alt-
Q] from the Terminal. [Alt-Z] can be your panic button if you forget the
[Alt-Q] key.
1.8 Creating and Running Your First Script
Most computer language tutorials begin by showing the student how to
print the message "Hello, World" to the screen. It's a quick way to
actually accomplish something, so we'll start by doing this here as well.
To begin, start Icom if necessary, then access the Script Manager by
pressing [Alt-U]. If [Alt-U] doesn't work, then you're in an area of
Icom where the hotkeys are disabled, and you'll have to exit that area
back to any 'major' Icom area, then try again. Note that these "follow
along while reading" bits are very quick, so if you're considering
printing this document now, don't (unless you don't mind doing so). For
99% of the tutorial you will simply read along in the online help or File
Viewer, (without wasting paper) without 'doing' anything but learning.
Just grab a piece of paper and write the few short script lines down
before exiting this document and entering the Script Manager. Writing
the commands down also helps the brain to remember things better.
From the Script Manager select "Create" to enter the editor (whether
internal or external) to create a new script.
Once in the editor type the two lines below, pressing the [Enter] key
after each line. If you make a mistake, use the backspace key (just
above the [Enter] key) to correct it. Case is not significant when
entering script commands; 'PRINT', 'Print', and 'print' all do the same
thing. Use whichever you prefer:
print "Hello, World!"
pause
Once you have these two lines entered, save the file as HELLO.SCR and
exit the editor (from Icom's internal editor, just press the [Esc] key to
exit, answer "Yes" when asked if you want to save your work, then enter
HELLO.SCR when asked for a filename. You're on your own if you're using
an external editor. See your editor's help screens if you don't know how
Intellicomm v2.01 SCRTUTOR.DOC 6
to save and exit.) If you're asked whether to overwrite an existing
file, someone probably gave you a copy of Intellicomm that had already
been "used" (they already read this and created their own HELLO.SCR). If
that's the case just overwrite the existing file when asked.
Once you return to the Script Manager you should see HELLO.SCR displayed
in the list of script filenames. To execute the script, move the top
hilight bar to HELLO.SCR with the [Up] / [Down] cursor keys (or move the
mouse cursor to HELLO.SCR and click), then select "Run" from the bottom
menu by pressing the [R] key, or by moving the mouse cursor to "Run" and
clicking.
Intellicomm should then switch to the Terminal screen, print the message
Hello, World! to the screen, and pause for a keystroke. When you press a
key the script will end and you will be returned to the Script Manager.
Did it work? If so, congratulations! If not, please start over, and
check your typing carefully. You may have missed a " quote or left a
space out accidentally.
1.9 A More Exciting Example
It's nice to be able to print the message "Hello, World" to the screen,
but it certainly isn't very impressive or exciting. Let's try something
a little more interesting this time.
You know what 'menus' are if you've been using Icom for any length of
time, and now it's time to demonstrate how easy it is to create a menu
using an Icom script. You may even impress your friends or co-workers
with this next script!
Select "Create" again from the Script Manager to enter the editor and
create a new script, then type in these three lines exactly, double-
checking all the quotes, spaces and tildes (~):
MENUDEFINE "Option ~1" "Option ~2" "-" "Option ~3" "Option ~4"
MENUBOXV "My Own Menu" "Your selection, Sir?"
PAUSE "You selected Option " $MENUSELECTION
When done, save the script to disk but this time as MENU.SCR (from the
internal editor, press [Esc], answer "Yes" to save it, then enter
MENU.SCR when asked for a filename; again overwrite if a MENU.SCR already
exists). When you return to the Script Manager, hilight MENU.SCR and
select "Run" to see what you've created.
If you entered the lines above exactly, you'll have created a centered
box menu, with title, four menu options with a divider line separating
items 2 and 3, hotkeys, and mouse support! Of course, the menu doesn't
actually 'do' anything yet other than print which option you selected (or
0 if you press the [Esc] key)... you'd have to add a few more lines to
the script to get the options to do something.
However, just being able to display such a menu on the screen with
working hilight bars and mouse support (and even a screen blanker, if
Intellicomm v2.01 SCRTUTOR.DOC 7
that feature is turned on in the Icom main setup) is no small programming
task. Yet you accomplished it in about 30 seconds! We used "Option ~1"
and so forth above, but the options can be any text you like; as long all
the options remain on the same line, each in double quotes, separated
from one another by a space. The "~" character tells MENUDEFINE where
the 'hotkey' is. Using a single hypen "-" as a menu option specifies a
divider line.
$MENUSELECTION is a 'System Variable' which Icom sets after menu-handling
commands to tell you which (if any) item was selected from the menu.
There are about a hundred of these System Variables available (all
starting with $) and each is documented fully in SCRIPT.DOC.
By testing the $MENUSELECTION variable with an IF or SWITCH command, you
could have various other commands carried out according to the menu
option the user selected. Example (text after a semicolon ; is ignored
by the script processor and is used to make comments for human
consumption):
;The first IF means "IF $MENUSELECTION is equal to 1" If it is, then
; the command following the IF is executed. If not, the command is
; skipped.
IF $MENUSELECTION = 1
print "Downloading file..." ;these commands are only
download "Z" "" ; executed IF $MENUSELECTION
...etc. ; is equal to 1
ENDIF
IF $MENUSELECTION = 2 gosub Item1Selected ;go to a subroutine
A better way to test the value of $MENUSELECTION though is to use the
SWITCH command. SWITCH is designed specifically to test a variable such
as $MENUSELECTION for multiple conditions, and to carry out one command
(or set of commands) according to the value of the variable. Read the
comments (after ;) for an explanation:
SWITCH $MENUSELECTION ;specify the variable to test
case 1 ;is $MENUSELECTION equal to 1?
print "Option 1 selected" ; yes, print this
... ; execute as many commands as you like
endcase ;'end of case 1' (the rest are skipped)
case 2 ;is $MENUSELECTION equal to 2?
print "Option 2 selected" ; yes, print this
... ; and execute whatever commands you like
endcase ;'end of case 2' (the rest are skipped)
... ;and so on with as many 'case/endcase' as
; needed
default ;default is an optional 'case' that is
... ; carried out if no other cases are true
endcase ;end of 'default' case
ENDSWITCH ;mandatory 'end of switch' statement
If $MENUSELECTION was equal to 1 (i.e. if the user had selected option 1
from the menu) then ONLY the command(s) between "case 1" and its
Intellicomm v2.01 SCRTUTOR.DOC 8
"endcase" would be executed. Note that "case 1" isn't fixed text. You
won't always use "case 1", "case 2", etc. In some cases you might use
this:
case ".ZIP" ;is the SWITCH equal to ".ZIP"?
endcase
case 12345 ;is the SWITCH equal to 12345?
endcase
...etc. Only the word "case" is fixed. Whatever follows the word "case"
is compared to the variable specified in the SWITCH ($MENUSELECTION in
this example) and if they're equal then the commands between that
case/endcase are carried out. If they aren't equal, the case (to its
ENDCASE) is skipped, and the next 'case' is checked.
When you're ready for more information on using menus, see the
MENUDEFINE, MENUBAR, MENUBOXH, MENUBOXV, IF and SWITCH in the DETAILED
COMMAND SUMMARY section of SCRIPT.DOC. Also see SCRDEMO.SCR and
POSTFILE.SCR for practical examples of menu and SWITCH usage.
1.10 Other Ways of Running Scripts
The scripts you just wrote, as with any Icom script, can be executed in
all sorts of different ways. You can run them from BIFs, from Jobs, from
the DOS command line (using a .BAT file or menu system, for example),
from another script, or from the Script Manager.
1.10.1 Running Script from BIFs
Scripts can be executed from a BIF by simply specifying @SCRIPTNAME as
the response to a prompt:
| Your Logon Name> @HELLO Name . . . . . . st Name? « |
^^^^^^
Another useful way to run a script from a BIF is via the "Connect
Command" item on BIF screen 1. This command (or @SCRIPTNAME) is executed
as soon as Icom connects to the BBS, before it gets any logon prompts.
1.10.2 Running Scripts from Jobs
Scripts can also be executed by a job "Custom Command" (defined in the
Icom Job Editor, where you define all your automated jobs) again by
specifying @SCRIPTNAME as the Custom Command:
| 12 Custom Command/Run script | CC: @MENU |
^^^^^
The above two methods of running scripts allow you to run any script just
about ANYWHERE during an automated job. Note that the .SCR extension is
not specified in the examples above. You 'can' specify the extension if
you like, but Icom supplies the .SCR extension if no extension is given,
so it's just extra typing to add it (though it can add clarity to specify
a .SCR extension). BIFs and Custom Commands are the only two places
where you must specify '@' before the script name, and the reason for
that is to tell Icom to run a script instead of doing what it usually
Intellicomm v2.01 SCRTUTOR.DOC 9
does: send the text you define to the BBS.
1.10.3 Running Scripts from DOS
Another way to run scripts is via the /scr: command line switch.
Example:
ICOM.EXE /scr:MENU
This example above would cause Icom to switch to Terminal mode, run your
MENU.SCR which we just created, display the menu (and handle any options
in it, if there were real commands hooked up to the menu), then exit back
to DOS as with the /run: command line switch, which does much the same
thing, but for automated jobs instead of scripts.
1.10.4 Running Scripts from Scripts
You can also execute a script FROM a script using the SCRIPT command.
Example:
SCRIPT "MENU" ;run MENU.SCR from another script
print "Hello, World!"
pause
If you ran the above script, MENU.SCR would be executed (until
completion... it could handle all sorts of different tasks) and when
MENU.SCR finished, the message "Hello, World." would be displayed. (The
Hello, World is simply used to show you that the original script
continues execution after MENU.SCR finishes.) Again, you can add the
.SCR extension if you like, but it's not necessary. Calling one script
from another is not something many people will have to do, but the
ability is there if needed.
Note that no @ is required in either of the above examples when
specifying the script name.
You can also run scripts from scripts by calling up the Script Manager
and allowing the user to hilight/Run one or more scripts, using the
SCRIPT command but WITHOUT specifying a script filename after the
command:
pause "We interrupt this script to bring you the Script Manager..."
SCRIPT ;the user could Tag/Run one or more scripts
; ...this script would continue below when all
; the other scripts were finished
print "We now continue with our regularly scheduled program..."
Note above that instead of using PRINT to print a message, and then PAUSE
to wait for a keystroke, both were combined into a single PAUSE command.
PAUSE accepts an optional message to PRINT before pausing.
1.10.5 Running Scripts from the Script Manager
Of course, scripts can also be executed from the Script Manager by
Intellicomm v2.01 SCRTUTOR.DOC 10
pressing [Alt-U] in Icom to access the Script Manager as you've just done
(the Script Manager can also be used when online, by simply pressing
[Alt-U] in Terminal mode... Again, it's on the [Alt-Z] terminal menu if
you forget the key). You can also Tag and Run scripts one after another
from the Script Manager, similar to what you can do with jobs from the
Job Directory -- though this is useful only if the scripts you Tag/Run
are DESIGNED to run one after the other. Further, Tagged scripts are
executed in the ORDER IN WHICH THEY ARE DISPLAYED in the Script Manager,
but you can easily change this order by renaming scripts (using symbols
or numbers, like 1SCRIPT.SCR, 2SCRIPT.SCR, or !SCRIPT.SCR to sort to the
top, etc).
1.10.6 Running Scripts via Function Keys
And if the above isn't enough variety you can also "attach" scripts to
function keys so a script executes when you press a key. This is best
accomplished using Script Learn which asks you whether to attach the
script to a function key or not. To attach a script to a key manually
you must look in the Appendix of the SCRIPT.DOC manual (or in the online
script help) for the "Key Code" of the function key, and must name the
script using the numeric key code for the function key to attach to.
Zeros must also be padded on the left side of the script name to make it
exactly eight characters long. For example if you looked up the Key Code
for the [F2] key you'd see that was 15360. To attach a script to the
[F2] key then, you would name your script 00015360.SCR. From that point
on, whenever you pressed the [F2] key from Terminal mode, 00015360.SCR
would be executed.
The leading zeroes MUST be added or the script will not execute when you
press the key (this avoids conflicts with other scripts that just happen
to use a number as the filename). Further you must use a function key
(the key alone, or in combination with [Alt], [Ctrl] or [Shift] ... they
all have unique Key Codes) and cannot use the [F1] key or any other keys.
[F1] is reserved for online help and for future expansion.
1.11 It Can't be That Simple, Can It?
Writing and using scripts can be as complicated or easy as you want it to
be. There are many EXTREMELY easy-to-use script commands available to
put loads of power into your hands with very little effort.
To prove this point further, let's assume that you want to dial a BBS,
make sure you're connected, perform a complete logon responding to every
question asked by the BBS, keep an eye out for the BBS main menu so
you'll know when the logon is complete, AND implement some sort of error
handling so that if something goes wrong and you don't reach the main
menu after, say, two minutes, you can give it up and hangup. To make it
interesting, you also want to hangup if a BBS event is scheduled, and if
you DO logon successfully, you want to capture BBS bulletin #5 to a file
called BLT5.TXT, then logoff and hangup.
If someone asked you to write a script to do this, what would you tell
them? You'd probably tell them to get bent (or to use Intellicomm's
built-in automation routines, which would make a lot more sense), but
Intellicomm v2.01 SCRTUTOR.DOC 11
you'll be able to do this on most any BBS just a few minutes from now.
It may seem a rather meaty assignment to be jumping to after "Hello,
World", and with some script languages you'd be in for a rather
complicated bit of script writing and concept learning to get the job
done. With Intellicomm you get most of the work done with three
commands: WHEN, SEND and WAITFOR. Here's the example (explanation
follows the example):
dial "Joe's BBS" 1 ;place the call
offline goto ExitScript ;jump to label ExitScript: if not connected
when "Enter Language #" send "1"
when "graphics (Enter)=no?" send "N"
when "st name?" send "John Smith"
when "Password" send "Mypassword"
when "Scan Message Base" send "N"
when "More?" send "N"
when "(Enter) to" send ""
when "upcoming EVENT" goto HangItUp
waitfor "Command? " 120 HangItUp
;the logon is now complete
when ;clear all the whens, no longer needed
cappush ;save capture filename/state (open/closed)
capture "BLT5.TXT" ;open a new capture file
send "B 5 NS" ;get bulletin 5, non-stop mode (PCBoard)
waitfor "Command? " 120 HangItUp
cappop ;restore the old capture file
send "G" ;send [G]oodbye to logoff
HangItUp: ; <-- here is where 'HangItUp' is
hangup ;hangup the modem
ExitScript: ; <-- here is where 'ExitScript' is
exit ;end the script
Just twenty lines gets the whole job done... and for demonstration
purposes the above script is actually more involved than it need be.
Ignore the technical details for now (you may still be wondering why
there are double quotes around some items, etc.) and just take it one
step at a time.
The DIAL command (the first command in the script) is used to dial a BBS.
The text following the dial command tells Icom which BBS to dial: in this
example Icom would search your BBS Directory for a BIF with a description
of "Joe's BBS", and if found it would Tag the BIF and "Dial" it. Note
the '1' following "Joe's BBS". This tells DIAL that the BIF description
must match exactly, and the '1' is optional. If the 1 is omitted, then
any portion of any BIF description that had the text "Joe's BBS" in it
("Joe's BBS 2", etc) would also be tagged.
Thus, you can also Tag/Dial multiple BIFs, depending on the descriptions
you use to save your BIFs. If you had ten BIFs that had exclamation
marks in their descriptions (Joe's BBS!, CRS!, Sound Advice!, etc), then
you could just specify the exclamation mark, and omit the '1' following
Intellicomm v2.01 SCRTUTOR.DOC 12
the description (i.e. do not force an exact match):
dial "!"
DIAL would then Tag/Dial all ten BIFs. When the BBS is connected to, it
is untagged automatically. REDIAL can then be used to dial the tagged
BBS's you haven't connected to, until all BIFs are untagged.
The easiest way to find the proper text to use after the DIAL command
("Joe's BBS" above), if you intend to tag several BBS's by omitting the
'1' in the DIAL command, is to enter Icom, press [Alt-D] to switch to the
BBS Directory and select "Find" from the BBS Directory menu. Then enter
the text you intend to use after the DIAL command ("Joe's BBS", without
the quotes), select "Find all/Tag all", and Icom will show you which BIFs
would be tagged if you used that text after a DIAL command. That's
exactly what DIAL does: it changes to the BBS Directory, then does a
"Find all/Tag all" on the text you followed the DIAL command with, then
selects "Dial" from the BBS Directory menu. By using certain keywords
(or exclamation points, etc.) in your BIF descriptions you can use this
to your advantage to dial multiple BBS's with a single DIAL command.
On to the next line: 'offline goto ExitScript'. Scripts execute from top
to bottom (first line to last) unless told otherwise by specific
commands: GOTO is one of those commands, and there are several others.
The OFFLINE command checks the modem to see whether it's online or
offline (connected or not connected). If the modem ISN'T offline (if we
got connected), then Icom ignores the GOTO command and simply continues
with the next line of the script. If the modem IS offline (we didn't get
connected), then Icom executes the command following the offline command:
GOTO in this case. I.e. an 'if' is implied before the OFFLINE command,
and you can think of it as "if OFFLINE do this". GOTO causes Icom to
goto (jump to) what is called a script 'label'. The label we're telling
Icom to goto is label ExitScript:, which gets us down to the end of the
script rather quickly (EXIT ends a script immediately). The only time
you follow a label with a colon (:) is when you're actually showing Icom
WHERE THE LABEL IS. I.e. where to GOTO. You don't follow labels with a
colon anywhere else.
I said GOTO causes Icom to jump TO the label, but that's not quite true:
it jumps to the next line following the label. The label itself can't be
executed, so that line is skipped and the script starts executing the
line just following the label. If labels are found in the normal running
of a script (we didn't GOTO the label; we just happened to run into it)
the label is ignored, but any commands BELOW the label are still
executed. If that's not what you want, then you simply put a GOTO above
the label to to jump around the commands. For example, if the script
above wasn't designed to logoff the BBS (we didn't want to hangup), we'd
have to jump around the HANGUP command with a 'GOTO ExitScript' just
above the label 'HangItUp:'.
We could have just used 'GOTO HangItUp' in the OFFLINE command and
eliminated the 'ExitScript:' label (the only place the 'ExitScript' label
is used is by the OFFLINE command). But there'd be no need to hangup the
modem if we were offline, so we put one label above the hangup, and can
Intellicomm v2.01 SCRTUTOR.DOC 13
GOTO there to hangup and THEN exit the script, and one below the HANGUP
so we can GOTO there and exit the script without hanging up. Using
labels and GOTO's, etc., is how you perform decision-making in your
scripts, causing some commands to execute under some conditions, and
other commands (or no commands) to execute under other conditions. We
also could have just used OFFLINE EXIT (if offline, exit the script...
which is simpler than jumping to the label below), but two labels and a
GOTO were used for demonstration purposes.
The next command we run across after 'offline goto ExitScript' is the
WHEN command. WHEN uses this format:
WHEN "you find this text" do this
('you' referring to Icom, and 'find' meaning if the text comes in the COM
port, from the BBS.) WHEN doesn't actually do anything on its own,
unless you specify it all by itself, with no parameters following it, in
which case it CLEARS all the WHENs defined previously. It's not until
WAITFOR is executed that the WHENs become active: WHEN and WAITFOR works
as a team. WHEN simply causes Icom to store the text and position of the
command following the WHEN in memory, for later use with WAITFOR.
WAITFOR activates all the WHENs (all prompts are then watched for
simultaneously... you can use up to 19 WHENs at a time) while WAITingFOR
specific text from the BBS. Waitfor uses this format:
WAITFOR "wait for this text" <how long> <label to GOTO if not found>
If the text "wait for this text" is found, the WAITFOR command ends and
the script continues with any lines following the waitfor (above we SEND
a command to get bulletin 5, open a new CAPTURE file, wait for the menu
again, then reset the previous capture file and logoff). The 120 in the
WAITFOR command in the script above tells WAITFOR how long to wait before
giving up (in seconds; 120 is two minutes) and 'HangItUp' tells waitfor
where to jump to (GOTO) if the text ISN'T found within the two minutes.
'HangItUp:', defined just below, skips the capture and executes the
HANGUP command to hang up the modem, then it runs into the EXIT which
ends the script. WAITFOR can also be used on its own, without WHENs, if
you don't need any BBS prompts handled while waiting.
If/when any of the text following any of the WHEN commands comes in from
the BBS while a WAITFOR is active, Icom executes the command specified by
that WHEN (and ONLY that command; none of the commands in the other WHENs
are executed until the text they specify is found). If the command
specified in the WHEN doesn't turn control over to another part of the
script (SEND just sends some text; GOTO turns control over to another
part of the script), then Icom just executes the command and continues
waiting for the text specified in the WAITFOR. If/when the text
specified by another WHEN is found, the same thing happens, and control
returns to WAITFOR until the timeout is reached. I.e. the WHENs are just
a temporary diversion while WAITingFOR the main objective: the main menu.
The WAITFOR timer is not reset after executing the WHEN commands. Two
minutes in total, no matter how many times we execute WHEN commands, is
how long WAITFOR will wait. I say "wait" because no other commands
(other than the WHENs) in the script can execute until the WAITFOR either
Intellicomm v2.01 SCRTUTOR.DOC 14
finds the text or times out. I.e. WAITFOR pauses script execution, and
the lines below a WAITFOR cannot execute until either the text is found,
or WAITFOR times out.
The commands used in WHEN statements are just regular Icom script
commands, and you can use any script command (or even a set of commands
if you use SWITCH, or IF/ENDIF, etc) with a WHEN. The SEND command, used
in most of the WHENs above, sends responses to the BBS (no different than
you typing the response at the keyboard).
Send can also be used on its own to simply send a few commands to the
BBS. Send automatically adds a carriage return (same as pressing the
ENTER key) after the text, so you needn't worry about adding one
yourself:
send "J 2" ;PCBoard, join conference 2
send "D" ;PCBoard [D]ownload command
send "ICOM201A.ZIP" ;filename to download
download "Z" ;download the file using Zmodem
The example above probably doesn't even need an explanation other than
the "Z" following the DOWNLOAD command. When downloading (or
uploading... UPLOAD is the command in that case), to select a protocol,
you specify the same letter that you'd press to select the protocol from
Icom's protocol menu. If you pressed [PgDn] in terminal mode, you'd see
that "Z" is the hotkey used on the protocol menu to select Zmodem. So
"Z" is what you specify after the DOWNLOAD command in your script to use
the Zmodem protocol. You can use other protocols (including any external
protocols you have set up) in the same way; simply by specifying the
protocol menu hotkey after the DOWNLOAD command. You could also do this
with a WHEN:
WHEN "Start your download" DOWNLOAD "Z"
If/when the text "Start your download" came in from the BBS during a
WAITFOR, Icom would begin a Zmodem download.
Zmodem doesn't require the name of the file to download since the
protocol itself (Zmodem at the BBS) passes the filename. The above
DOWNLOAD command could even perform a 'batch' Zmodem download, since all
the logic to perform multiple-file downloads is built into the protocol.
No filenames need ever be specified when downloading with Ymodem OR
Zmodem; the filename(s) are specified by the sender. Xmodem and its
cousins Xmodem-1K and Xmodem-1k-G do require a filename, since the Xmodem
protocol wasn't designed to pass filenames. Further, since Xmodem
doesn't pass the filename it cannot perform multiple-file (batch)
downloads. If using an EXTERNAL protocol it's up to you to know whether
the protocol requires a filename on downloads (if it does require a
filename, the name is specified immediately following the protocol
letter; Icom will pass the filename to the protocol). *ALL* protocols
require filenames for uploads:
UPLOAD "Z" "ICOM201A.ZIP" "ICOM201B.ZIP"
UPLOAD "Y" "*.ZIP" "D:\TEMP\A???.*"
Intellicomm v2.01 SCRTUTOR.DOC 15
The first command would upload ICOM201A.ZIP and ICOM201B.ZIP, using
Zmodem. The second would upload any files with a ZIP extension (anywhere
on the Icom 'Upload PATH' defined in the main setup or current BIF), and
any file in the D:\TEMP directory with an 'A' followed by any 3
characters, and any extension. [Regular DOS wildcards... check your DOS
manual if you aren't familiar with wildcards.] Again, you can only
specify multiple filenames with Ymodem (or Ymodem-G) and Zmodem, or an
external protocol that supports 'batch' uploads. Xmodem does not
support batch uploads or downloads.
You can also create a 'list' of filenames using the Text Editor, then
have the protocol use the list to get the filenames to upload:
UPLOAD "Z" "@FILES.TXT"
Zmodem would read FILES.TXT, and upload all filespecs on the list. Each
filespec may contain a drive/path, and may use wildcards. When using
text lists, you specify each filespec on a separate line of the file:
C:\TEMP\SOMEFILE.ZIP
A???.*
*.ZIP
Note that these rules also apply when uploading manually... you can do
all of the above (minus the quotes) when specifying filenames at the
Upload filename prompt.
1.12 Variable Overview
Variables are a very useful script feature, and can increase the
usefulness and flexibility of your scripts significantly. They're not
something you 'have' to know about to write scripts, but are something
you probably will WANT to know about, since they're extremely useful.
Variables are nothing more than "places to put things". Some variables
come with 'things' already in them (information which you can use in your
scripts), while other variables are created by you (via the VARIABLE
command) to allow you to keep track of your own 'things'.
If you wish to count something, such as the number of times a certain
error happened, a variable is needed. If you wish to know what time it
is, you use another variable. There are six different types of variables
you can make use of in your scripts, and only a quick overview of
variables is given in this section. Detailed information on all six
types of variables, plus many examples of usage, can be found in section
4 below.
System Variables
~~~~~~~~~~~~~~~~
All System Variables begin with a dollar sign ($). They are used to
access all sorts of information about your computer system, and the
current status of Intellicomm itself. They also make your scripts much
more flexible, since they allow you to avoid specifying constant data.
Examples:
Intellicomm v2.01 SCRTUTOR.DOC 16
SEND $PASSWORD ;send the BIF logon password
PRINT $DATE ;prints the current date
IF $DOW = 1 PRINT "Today is Sunday" ;$DOW is the Day-Of-Week
IF $DOW = 2 PRINT "Today is Monday" ; ...etc.
The first IF means 'if the day-of-week ($DOW) is equal to 1 (Sunday)',
then PRINT "Today is Sunday". If $DOW is NOT 1, the PRINT following the
IF is ignored.
There are a hundred or so different System Variables available and all
are outlined quickly in the SCRIPT COMMANDS AT A GLANCE section, and (in
more detail) in an appendix at the end of the SCRIPT.DOC manual. Anyone
and everyone can and should use System Variables... they're very
straightforward and useful. As with all the other types of variables
listed below, you can use a System Variable in ANY script command that
takes a parameter, instead of using constant text or a constant number.
User-Defined Variables
~~~~~~~~~~~~~~~~~~~~~~
These are variables you define yourself using the VARIABLE command. If
you wish to count something, you might define a variable called 'count':
VARIABLE count ;this command defines variable 'count' and
; assigns 0 to it (0 is the default)
VARIABLE ten 10 ;this command defines variable 'ten' and
; assigns 10 to it.
INC count ;INCrement count (count = count + 1)
ADD count ten 5 ;count = 10 + 5
If you wish to store your name, you might use this:
VARIABLE LogonName "John Smith"
Later in your script, instead of using SEND "John Smith", you could use
SEND LogonName. These variables also allow you to avoid 'hardcoding'
information into your scripts, and thus they allow a single script to
serve more than one purpose.
Instead of DIAL "Joe's BBS", you can get user input from the keyboard,
store the input in a variable, then use the exact same script to dial
multiple BBS's. Further, these variables also make your scripts easier
to maintain. If you define all your variables in one section of the
script, and later something changes (a BBS prompt, for example), you can
simply change the value assigned to the variable without having to change
dozens of WHEN commands. "Variables" are also what enables Intellicomm
to automate calls to different BBS types. It simply loads the BIF
information into its variables... and the program then behaves
differently, since it's using different data.
Many script commands also REQUIRE a variable as the first parameter, in
order to store the results of an operation. For example the ADD command
adds two numbers and stores the result in a User-Defined Variable. The
GETS command gets input from the keyboard, and also stores the input in
Intellicomm v2.01 SCRTUTOR.DOC 17
any User-Defined Variable:
VARIABLE myvariable ;define a variable called 'myvariable'
GETS myvariable 60 ;get (max) 60 characters from the keyboard, store
; the input in 'myvariable'
PRINT myvariable ;print the result
User-Defined Variables exist only from the time of their creation with
the VARIABLE command until the current script ends. They are also
'local' to the current script, so if you execute another script FROM a
script with the SCRIPT command you needn't worry about changing the
contents of the variables in the first script.
The Global Variable Array (GlobalStr)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is a variation of the User-Defined Variables. But the 'Global'
Variables exist in memory for the duration of the Icom session. Unlike
the User-Defined Variables, these variables are 'globally' available to
all scripts, and still hold their values after the script ends (used
mainly for inter-script communication and the like). They also employ a
concept known as an 'array'.
The GlobalStr array is where script command line parameters are stored,
if you pass parameters to a script from a BIF command, or job Custom
Command, or from the DOS command line. Use of the GlobalStr array is
more advanced than most other script concepts and will probably only be
needed by advanced script writers.
BIF Variables
~~~~~~~~~~~~~
These variables allow you to access any information from the BIF
currently in use (the BBS you're currently connected to, or the BIF you
explicitly load with the LOADBIF command). BIF variables are also
somewhat advanced and probably won't be needed immediately. See the BIF
VARIABLES section in SCRIPT.DOC when you're ready for more information.
Main Setup Variables
~~~~~~~~~~~~~~~~~~~~
These variables allow you access any information from the INI
(INItialization file) currently in use; normally ICOM.INI (you can also
load Main Setup files with the LOADINI command). The Main Setup
Variables contain all the main data you define in the Intellicomm Setup.
Again, these variables are somewhat advanced and you can leave them for
the time being. When you're ready for more information, see the MAIN
SETUP VARIABLES section in SCRIPT.DOC.
Permanent Variables
~~~~~~~~~~~~~~~~~~~
Permanent Variables make use of the script "File Input/Output" commands
to store information on-disk. The concepts aren't overly difficult to
grasp, but again won't be needed by everyone. See the INTRODUCTION TO
FILE INPUT / OUTPUT, here in SCRTUTOR.DOC, when you're ready for more
information.
Intellicomm v2.01 SCRTUTOR.DOC 18
1.13 A Word on Formatting
You may have noticed above in some of the example scripts that certain
items 'lined up' with each other (the SEND commands after the WHENs, for
example), and that the comments are in a certain position, etc. But this
is done only to please the eye, and the only rules that are forced on you
as far as formatting goes are these:
1. Every script command (including any parameters that follow the
command) must be on a separate line. EXCEPTION: If the script command
is designed to take another command as one of its parameters (such as
the OFFLINE and WHEN commands demonstrated above), then you may (and
must) place more than one command on a single line. Just don't try to
do this:
dial "Joe's BBS" offline exit ;both on the same line
If a command accepts another command as a parameter, the Detailed
Summary of the command will tell you so. If a command isn't expected as
a parameter, you can't use one.
2. At least one space or TAB character must separate the script command
from the parameters (if any), and at least one space/TAB must separate
each parameter. Example: COMMAND "parameter 1" "parameter 2" ...
These are errors: COMMAND"parameter1" or COMMAND "parm1""parm2".
^ ^
One or more spaces (or a TAB) are needed where the carets (^) are.
3. Only the first 256 characters of each line are used by the script
processor. You may create lines that are longer than 256 characters
(comments that go past the 256 character mark, etc.), but only the
first 256 characters are significant to the script processor. This
will probably only be relevant when using the MENUDEFINE command,
which uses the format: MENUDEFINE "Menu Item 1" "Menu Item 2" ...
etc. You'll have to make sure to fit all your menu items in the first
256 characters for it to work properly. If you run into problems, use
variables instead of literal strings and use short variable names.
Example:
VARIABLE i1 "Menu Item ~1"
VARIABLE i2 "Menu Item ~2"
... ; "..." denotes more of the same
MENUDEFINE i1 i2 ...
4. Script 'labels' used to define where a GOTO (and GOSUB) should jump to
must on a line by themselves, must be followed by a colon, and there
must not be any spaces or tabs anywhere in the label right up to the
colon. The only characters you can use in labels are A-Z, a-z, 1-9,
and the underscore (_). Labels are not case-significant (LABEL: is
the same as label:). NOTE: comments are permitted after labels, but
you must put at least one space or TAB between the label and the
comment. Example:
HangItUp: ;this label HangItUp
Intellicomm v2.01 SCRTUTOR.DOC 19
5. Comments must be preceded by a semicolon (;) and may appear on the
same line as a script command or label (after the command/label and
all parameters), or may be placed on a line by themselves. Comments
must also be separated from the command/parameters by at least one
space or TAB. These comments are invalid:
HangItUp:;This comment isn't separated from the label
dial "Joe's BBS";This comment isn't seperated from the parameter.
These comments are fine:
HangItUp: ;This is fine with a space
dial "Joe's BBS" ;This is fine with a space
All characters following a semicolon (to the end of the line) are
ignored by the script processor. The one exception to this is if the
semicolon is inside quotes.
1.14 Why the Double Quotes ""?
Double quotes, such as the quotes surrounding the text after the WHEN and
SEND commands above are used (a) to keep all the text together as a
single unit/parameter, (b) to allow you to use text between the quotes
that would otherwise 'mean' something to the script processor
(semicolons, for example, which cause Icom to ignore the rest of the
line, and spaces or tabs which signify the end of the parameter), and (c)
to tell the script processor that it's dealing with literal text instead
of a variable. Variable names are never put in double quotes.
A quick note: if the text you're putting between the double quotes has
one or more double quotes IN it, then you must 'escape' the double quote
with a caret. Example:
"This text has ^"double quotes^" in it"
Quotes are never used to surround script commands or labels. They're
only needed in the parameters (if any) following script commands, and
only if the parameter is specifying constant TEXT (as opposed to a
variable name, or a constant number containing only characters from 0-9).
1.15 Specifying Control Characters
To specify a control character in a script command parameter (backspace,
carriage return, escape, line feed, form feed, Ctrl-A, Ctrl-B, Ctrl-C,
etc., are 'control characters') simply precede the control character with
a caret (^). For example, Ctrl-A would be specified as "^A", Ctrl-B is
"^B", escape is "^[". Refer to the ASCII table in the appendix of
SCRIPT.DOC (or in the online help) for the complete list of control
character codes. Here's an example of sending two escape characters to
the BBS (SENDNC sends without adding a Carriage Return. The NC means
'N'o 'C'arriage Return):
SENDNC "^[^[" ;SENDNC doesn't add a CR as SEND does
Intellicomm v2.01 SCRTUTOR.DOC 20
If you have to use a caret (^) for some reason, you must use TWO carets
or the script processor will either ignore it (if used alone), or will
convert the next character to a control character:
PRINT "^^" ;print a single caret
To enter the control character ^^ (ASCII code #30), hold down the [Alt]
key and type 3 then 0 on the NUMERIC KEYPAD, then release the [Alt] key.
1.16 Tildes
You can cause a one second pause between characters with SEND/SENDNC, or
SENDNP/SENDNPC by using a tilde (~):
SENDNC "^[~^[" ;send ESC, delay for 1 second, send another ESC
A delay is almost ALWAYS required by BBS front ends which require you to
press ESC twice before logging on. For some reason, they don't seem to
accept two rapid ESCapes. Again if you want to send a tilde, escape it
with a caret:
SENDNC "^~" ;sends a tilde (~)
Tildes are only relevant (and need only be escaped with ^) when you use
them with SEND/SENDNC or SENDNP/SENDNPC (i.e. when sending data out the
COM port). You needn't and shouldn't escape tildes with a caret in any
other script commands.
TIP: If you're thinking of writing logon scripts to use in your automated
jobs just for this purpose of sending ESC characters to bypass a BBS
front end, it is not needed. Just specify the ESCapes in the BIF
"Connect Command" option on the 1st BIF screen:
| Dial Prefix . . Connect Command ^[~^[
|
The above Connect Command causes Icom to send an ESC, pause for one
second, then send another ESC as soon as it connects to the BBS. This
should get you by most all BBS front ends.
1.17 Specifying Numbers
When specifying constant numbers in scripts (i.e. in math operations, or
when a number is expected as a parameter as with the timeout in WAITFOR)
you needn't and shouldn't put quotes around the numbers:
GOTOXY 1 1 ;move the screen cursor to the top left corner
Why? Well, numbers never use quotes or semicolons or spaces in them as
constant 'strings' (text) might, and they never conflict with variable
names since you can't specify a number as the first character of a
variable name.
Further, you must not use symbols or commas in the number, unless the
Intellicomm v2.01 SCRTUTOR.DOC 21
parameter is to be used as a 'string' and is in quotes (i.e. if using it
in a PRINT or WHEN or other command that takes TEXT as a parameter rather
than a number). As soon as the script processor finds a non-numeric
character in a parameter -- when it's expecting a NUMBER as a parameter -
- it ignores all following characters:
ADD result $5.00 $10.00 ;this causes an error, since $ signifies
; a System Variable. Icom would look for
; System Variables called 5.00 and 10.00
; ...wouldn't find them, and would abort
; the script with an error.
ADD result "50.5" 30 ;'result' will be 50 + 30 (the quotes are
; stripped, the "." is not a number and is
; thus ends the 1st number)
PRINT "$5.00" ;text is expected by PRINT, and is in quotes,
; so this is fine
ADD result 2,000 3,000 ;'result' will be 2 + 3 due to the commas
WHEN "2,000" send "3,000" ;text is expected by WHEN and SEND, and is in
; quotes, so this is fine
This version of the script language automatically strips quotes from
numbers if you use them... so this is 'legal':
GOTOXY "1" "1"
But the above is not good programming practice and isn't a good habit to
get into. Most programming languages consider it an error to put
constant numbers in quotes, since numbers are stored differently by
computers than text/strings are. You should get into the habit of
leaving the quotes off when specifying constant numbers, even though it
really makes no difference to this version of the script language.
1.18 Numeric Limits
Constant numbers (or numbers stored in variables that you plan to use in
place of a constant number) are limited to the range:
-2147483648 to 2147483647
That's about two billion negative and positive. The limits may look like
they were picked from a hat, but believe it or not those are 'round'
numbers to the computer. Computers don't count in decimal (base 10) like
we do, so you'll rarely find a number rounded to the nearest 10, 100,
etc. The above are the limits of a four "byte" (32 bit) digit. If you
go past the limit in either direction (i.e. add, subtract, multiply or
divide numbers that end up with a result higher or lower than the limits
above), the result will not be valid.
1.19 Compound Statements
Compound Statements, while the term might sound scary, are simply groups
of script commands on more than one script line, that 'act' similar to a
single script line. Three commands allow compound statements, and each
of the three has a 'closing' command to signify the end of the compound
Intellicomm v2.01 SCRTUTOR.DOC 22
statement:
Command Closing Command
---------------------------------
IF ENDIF
SWITCH ENDSWITCH
WHILE ENDWHILE
Here's an example:
variable x
variable y
IF x = y
print "x is equal to y"
pause "Please press a key..."
return
ENDIF
You'll notice above that three commands were placed between the IF and
ENDIF. If the comparison turned out to be TRUE (if x was equal to y),
then all three commands would be executed. If the comparison turned out
to be FALSE (if x was NOT equal to y) then everything between the IF and
ENDIF is skipped.
You can also "nest" compound statements, or in other words you can place
compound statements INSIDE of other compound statements like this:
IF x > y ;is x greater than y?
IF x = 0
print "x is equal to zero"
return
ENDIF ;end of the second IF
WHILE x > y
print x
dec x ;DECrement (x = x - 1)
ENDWHILE ;end of the WHILE
ENDIF ;end of the first IF
The first IF above works in exactly the same way as the first example.
If x is greater than (>) y then the commands between the first IF/ENDIF
pair are evaluated. If x is NOT greater than y, then everything is
skipped right down to the last ENDIF. There is no limit as to how many
compound statements you can nest, and the second IF above could have
another IF, SWITCH or while before its ENDIF, and so forth.
It's important to understand that these compound statements ACT as a
single script command, for cases such as these:
ONLINE SWITCH next_job
case 1
gosub do_job_1
endcase
case 2
Intellicomm v2.01 SCRTUTOR.DOC 23
gosub do_job_2
endcase
ENDSWITCH
Above if the modem WAS online, then the SWITCH compound statement (to the
ENDSWITCH, executing one or none of the 'cases') would be evaluated. If
the modem WASN'T online, then the entire SWITCH (along with any nested
compound statements WITHIN the SWITCH/ENDSWITCH) is ignored. This is
also legal:
ONLINE IF x = y SWITCH
... ;CASE/ENDCASEs here, along with even more
... ; compound statements if need be
ENDSWITCH
Note that if a command is specified after the IF comparison (after 'x =
y' above), then it is assumed that you are using the stand-alone version
of IF, instead of using an IF/ENDIF compound statement. Example:
IF x = y PRINT "x = y"
IF x = y
PRINT "x = y"
ENDIF
Both of the above do the same thing... One uses the stand-alone IF
variation (where only one command, or a compound statement is accepted
after the comparison) and one uses the IF/ENDIF variation. In the
previous example, the SWITCH is legal after the IF comparison since
compound statements (and nested compound statements) behave like single
script commands. Thus, if the modem was NOT online then everything right
to the ENDSWITCH would be skipped. If the modem WAS online then the IF
would be evaluated... and again if x was NOT equal to y, then everything
to the ENDSWITCH would be skipped. If x WAS equal to y, then the SWITCH
would be evaluated. Nested commands (compound or otherwise) progess one
step at a time, and whenever something proves to be false, the very next
command, be it a single command, a compound statement, or multiple nested
compound statements, is skipped.
Only after you gain a bit of experience will you want to get into
compound statement nesting, but you're bound to run into it sooner or
later (and it certainly can be quite useful at times) so it's handy to
know how they work.
1.20 Where To Go From Here
Congratulations! This ends the introduction. You could quit with this
knowledge right now and write some very useful scripts indeed. You know
what scripts are, how to write them, how to run them in every way
imaginable, how to display information on the screen, including working
menus, how to dial a BBS, how to watch for BBS text/send responses, how
to perform error handling if key BBS text you're looking for isn't found,
how to upload and download files, how to hangup the modem, how to perform
Intellicomm v2.01 SCRTUTOR.DOC 24
basic decision-making (GOTO), how to define and use variables, and in
general how to use compound and nested compound statements! And you
should be able to accomplish just about anything with these skills.
There are many more script commands in Icom's script language not
demonstrated above that are just as easy to learn and use, just as
useful, and will require little explanation to use. If you understood
the examples above you'll have no trouble understanding most of the other
script commands... It's only a matter of time and hands-on experience
until you master scripts completely. And when you master scripts, a
whole new world of possibilities will open up to you!
Your next step should be to execute the SCRDEMO.SCR (script demo) script
if you haven't already, and to then "Edit" SCRDEMO.SCR to see how various
tasks were accomplished. When you see a command you're not familiar
with, either use the online help "SCRIPT.DOC" link, or the File Viewer
(accessable in the Editor via the File|DOS/Open... menu option) to look
up the detailed summary of any commands you don't understand, or simply
want to read up on.
All script commands and System Variables are outlined in the SCRIPT
COMMANDS AT A GLANCE section of SCRIPT.DOC, and each command is explained
in detail in the DETAILED COMMAND SUMMARY section. EXAMPLES of usage for
each command can also be found in the Detailed Summaries.
1.21 The Secret to Success
The only real problem you have when writing scripts is in remembering the
commands, and remembering what 'parameters' follow the commands (like the
"Z" after the DOWNLOAD command). The trick is to ONLY concentrate on the
commands you NEED, and to learn (then USE) only one or two commands at a
time. Practice makes perfect; take one or two script commands that you
need, practice with them and get a script WORKING with them, and only
then move on to other commands you may need. You WILL succeed if you
take this approach.
Most people will use only 5% or 10% of the script commands, and will
never bother the other 90% (though 'which' 10% of the script language
each user concentrates on varies from person to person, depending on
their needs) so don't bother with a command until you need it. Perhaps
start with WHEN and SEND, to define your own logon script (note that you
MUST define a password in your BIF or Icom will not attempt an auto-
logon). Practice calling the logon script from a BIF by placing
@SCRIPTNAME in the BIF "Connect Command" item on BIF screen 1:
| Dial Prefix . . 1 Connect Command @SCRIPTNAME |
SCRIPTNAME.SCR would be executed as soon as Icom makes a connection at
that BBS. Try getting the script to answer just one or two logon
questions such as your name and password ... get that working, THEN move
on to other things. Or you could practice running a script from one of
the BIF Logon responses:
| Your Logon Name> @SCRIPTNAME Name . . . . . . st Name? « |
Intellicomm v2.01 SCRTUTOR.DOC 25
SCRIPTNAME.SCR in the example above would start with:
SEND "John Smith"
since Icom would already have found the "st Name?" prompt ... The script
might then continue to define some WHENs to handle other logon prompts:
WHEN "dots will echo)?" SEND $PASSWORD ;sends the BIF password
WHEN "last read" SEND "N"
WAITFOR "Command?" 120
Icom will pick things up wherever your script leaves off (assuming the
proper BIF Logon information is defined). You can run a script to answer
a SINGLE logon prompt, or you can handle the entire logon (even carrying
out a side-job such as capturing the BBS news file, etc.) and leave Icom
at the BBS Main Menu. The same applies to mail runs, bank transactions,
file transfers, etc. You can execute a script in ANY BIF response slot.
As long as the script carries out (at minimum) what the internal BIF
command its replacing did (opening a DOOR, etc), you should be fine.
Only when you become familiar with these basic concepts and gain
experience should you move on to more complex scripts (if you want to
learn more... stop whenever you're satisfied with what you've learned).
Browse the SCRIPT COMMANDS AT A GLANCE section often in the beginning,
just so that you'll at know what commands ARE AVAILABLE for specific
tasks. Don't try to memorize any commands... just give it a quick browse
so you'll know, in general, what is available. Only when you NEED that
task done in a script will you have to bother learning how the command
actually works. Imagine the script language as a big plate of assorted
appetizers, meant to serve everyone: look the plate over (the SCRIPT
COMMANDS AT A GLANCE section), and take what you want.
With just a little time and experience, you'll be able to write very
powerful scripts that will AMAZE you and give you great satisfaction.
Enjoy yourself, and be proud of your accomplishments! Writing scripts
that WORK, no matter how insignificant the task, can be a very satisfying
way to spend an evening or two.
Intellicomm v2.01 SCRTUTOR.DOC 26
2. WHAT HAPPENS WHEN A SCRIPT ENDS?
The short answer to what happens when a script ends is, "hopefully the
right thing, for whatever circumstances the script left the system in".
The longer answers follow:
Scripts end when one of four things happen: (1) the end of the script is
reached (there are no more commands to execute; this is true even if a
subroutine was executed and you forgot to add a RETURN), (2) Icom runs
across an EXIT or RETURN or SYSTEM command in a script, which ends the
script at that point, (3) the connection is lost and Icom was running an
automated job prior to calling the script, (4) the user aborts the script
by pressing [Alt-Q] then selecting "Abort Job/Script" or "Abort Script
Only" from the control menu. If the user selects "Abort Job/Script" then
both the script and the automated job are cancelled.
When any of the four events above occur, what Icom will do next depends
on whether your modem is connected or not connected (online or offline),
where you called the script from, and whether an 'errorcode' was returned
by the script in the EXIT or RETURN command.
If you run a script from an automated job via a Custom Command, the first
thing Icom will do when the script ends is attempt to locate its position
on the BBS by sending the BIF "General Menu Exit" command, then watching
for a BBS menu (the script may have changed locations, so Icom can't
assume it ended up where it was when the script started). If Icom
successfully locates its position, the next job task continues (Get Mail,
etc., or perhaps even another Custom Command that runs another script).
If you call a script from a BIF, by using @SCRIPTNAME in one of the BIF
command responses, Icom expects that YOU have made sure that your script
did the appropriate thing, and left Icom in the location it needed to be
in for whatever it'll be doing next (the same as any command you define
in a BIF; you can define anything you want, but if it doesn't do the
right thing, your job will probably fail).
If you call a script from the Script Manager, after the script stops
running Icom checks to see whether you're online or offline, and if
online it leaves you in Terminal mode to continue with your BBS session.
If the modem is offline when the script ends, Icom returns to the Script
Manager (unless you originally entered the Script Manager from Terminal
mode using [Alt-U] ... in that case, as with most 'sub-tasks' you execute
from Terminal mode, you are returned to the terminal when the script
ends).
If you run a script from DOS via the /scr: command line switch:
ICOM.EXE /scr:MYSCRIPT
Icom automatically returns *to DOS* if the modem is OFFLINE when the
script ends; but not until processing ALL command line switches. This
command is legal:
Intellicomm v2.01 SCRTUTOR.DOC 27
ICOM.EXE /scr:MYSCRIPT /run:"Some automated job"
Icom would run the script, then the automated job, and THEN if the modem
was offline it would return to DOS (note that Icom always saves /Run:
switch until ALL other command line parameters are processed. If you put
the /run: first, and the /scr: second, the script would still be executed
first). If you do something like the above, your script would have to
perform the dialing/logon, and would have to either leave Icom connected
to the BBS that the job is set up to run on, or would have to HANGUP
before exiting so that the job would dial. If Icom is connected when a
job is started it assumes you've already connected to the proper BBS and
simply begins the job without dialing. If you aren't connected to the
proper BBS, the job will probably fail.
This is also legal:
ICOM.EXE /scr:"SCRIPT1 parm1 parm2" /scr:"SCRIPT2 parm1 parm2"
(See section 4.10 for more information on passing parameters to scripts.)
If the modem is ONLINE when the script ends (rather, when all command
line options are processed) then Icom switches to manual terminal mode.
If the modem is OFFLINE, Icom exits back to DOS. If you're designing a
script to be called with the /scr: command line option via a .BAT file or
menu system (or Windows/DESQview), make sure that you execute the HANGUP
command before exiting your script, if you want Icom to exit back to
where it was called from (back to DOS, the .BAT file, or menu system).
2.1 EXIT and RETURN Error codes
If you use an EXIT or RETURN command in your script, and specify a
negative number as the exit code (EXIT -1, EXIT -2, RETURN -1, RETURN -2,
etc), then Icom cancels all automated jobs, hangs up, and returns to
whatever called it (the Job Directory/Main Menu if an automated job, the
Script Manager if called from there, or DOS if called via the /scr:
command line option). Using a negative exit code after EXIT or RETURN is
a rather potent option that should only be used when the circumstances
warrant it.
If a positive number follows the EXIT or RETURN command (EXIT 1, EXIT 2,
RETURN 1, RETURN 2, etc.) then Icom removes the current BIF from the job
queue, hangs up, and won't carry out any further jobs on that BBS (if
multiple jobs were Tagged for the current BBS) during the current
automated session. Use this one when you run out of time at a BBS, or an
EVENT is scheduled, etc., and you don't want to call that BBS back.
If zero (0) or no error code is specified after the EXIT or RETURN
command (EXIT 0, RETURN 0, or just EXIT or RETURN alone), or the script
simply reaches the end without any EXIT/RETURN at all, then it's a
regular exit and the the usual rules that govern what happens when a
script ends (outlined above) apply. This is what will be used in 99% of
the cases to end a script.
NOTE: This only applies to the RETURN command when it's used to actually
Intellicomm v2.01 SCRTUTOR.DOC 28
exit a script: I.e. if the RETURN is found OUTSIDE of a subroutine (a
GOSUB command was not executed previously). If you're simply RETURNing
from a subroutine, error codes are ignored. EXIT is used to exit all
scripts (if you call a script from a script and want to abort BOTH
scripts) while RETURN is used to simply exit the current script back to
the caller.
2.2 Using the HANGUP Command for Error-Recovery
If your script is called by an automated job (Custom Command or BIF
command) and loses track of its position or just can't get out of a
tricky situation, the easiest way to recover is to execute a script
HANGUP command. This will cause the modem to hangup, and when the
connection is lost Icom will abort the script, notice the carrier loss
(if during an automated job) and will call the BBS back if any job tasks
remain unfinished. Losses of connection are tracked by Icom, and it will
only permit *three* losses of connection before removing the BIF from the
job queue. If you use the HANGUP command from a script during an
automated job, it simply looks like a regular loss of connection to
Intellicomm.
3. INTRODUCTION TO SUBROUTINES (GOSUB)
Subroutines are indespensible in any programming language. They allow
you to carry out complicated tasks with a single script command -- GOSUB,
short for "GO" to "SUB"routine. Subroutines are also useful in cutting
down on programming/typing errors, since you can write and debug a
routine ONCE, then make use of that same bug-free routine in several
places in the script. Without subroutines you would be forced to write
repetitive sequences of script commands multiple times, thus increasing
the chance of making a typing error, or introducing a bug.
3.1 When to use a Subroutine
Subroutines should be used to replace repetitive sequences of script
commands. For example, if you're doing something like printing a 'Press
a key' message, then clearing the screen, you might as well use a
subroutine to avoid repeating the exact same commands in multiple places
in the script:
PressAKey:
PAUSE "Press a key... " ;wait for a key press
CLS ;clear the screen
RETURN ;return from the subroutine
Once the subroutine is in place, whenever and wherever you need to pause
for a keypress in your script, you just execute the command:
GOSUB PressAKey
When the script processor runs across a GOSUB command it first saves its
current position in the script (the position of the GOSUB command), then
it does the equivalent of a GOTO (jumps to the label specified after the
Intellicomm v2.01 SCRTUTOR.DOC 29
GOSUB) and begins executing the script at the next line after the label.
When a RETURN statement is found, it then restores the position it saved
previously, and continues running the script at the next line below the
GOSUB command.
Subroutines can also be used to make a script easier to follow and thus
easier to maintain (add to) and debug. They can also help you to
organize your thoughts when you have a complex task to accomplish.
Instead of trying to figure out how you're going to fit a complicated set
of commands into the EXISTING structure of your script, you can instead
write the complicated code elsewhere (concentrating only on one task at a
time) in a subroutine, without making a mess of the existing structure of
your script.
Finally, you can also do the equivalent of inventing new script commands
with subroutines. Computer (and script) languages give you "building
blocks" in the commands they provide. You won't always find a single
command that does exactly what you need, but you can usually get
virtually ANY job done by combining two or more commands and placing them
in a subroutine. Once the subroutine is written, it becomes basically as
easy to use as a built-in script command is.
3.2 Writing a Subroutine
Writing a subroutine is no different than writing any other portion of
your script, with two exceptions: you must think up a 'name' for a
subroutine, and you must use a RETURN command to exit the subroutine
(back from where it was called). Example:
MySubroutine:
print "I am now in 'MySubroutine'"
return
The above subroutine is called 'MySubroutine' and it could be executed
from anywhere in a script using the command:
GOSUB MySubroutine ;note that the colon is not specified
Subroutines can be placed anywhere in your script, though the most common
place to put new subroutines is right at the end of the script. The
reason they will normally be placed at the 'end' is so you can easily
avoid running into them by placing an EXIT (or RETURN) command just above
the beginning of all your subroutines. Example:
GOSUB InitScript ;Initialize (define and set up any variables)
... ;main body of script here
EXIT ;EXIT (or RETURN) after the main body
;Now you can't run into 'InitScript' accidentally
InitScript:
...
RETURN ;and you can't run into 'Subroutine2' accidentally
Intellicomm v2.01 SCRTUTOR.DOC 30
Subroutine2: ;and so forth...
...
RETURN
Subroutine3:
...
RETURN
Remember that the script processor simply ignores 'Labels:' if if happens
to run into them in the normal course of running a script. Without the
EXIT and RETURNs above, all the subroutines would be executed when the
main body of the script ran into the subroutine labels below it.
3.3 GOSUB In Detail
When the script processor runs across a GOSUB command, it first saves its
current position (the 'current' position will be the END of the line on
which the GOSUB was found), then searches from the top of the script for
the 'Label' you specify AFTER the GOSUB command. If the label is found,
execution of the script then begins on the next script line after the
label (labels, which are always followed by a colon, are always ignored).
The next time a RETURN is found (whether it was actually in the
subroutine or not; you can GOTO out of one subroutine and execute
another, then RETURN from there) the position saved when the GOSUB found
is then 'popped' off the GOSUB stack, and script execution returns to the
next command after the *last* GOSUB (if any).
The GOSUB 'stack' (the place where the script processor saves the
position of GOSUB commands, before executing the subroutine) can hold 16
positions. Thus, you can execute up to 16 'nested' GOSUB commands (GOSUB
from *within* a subroutine) before executing a single RETURN. If you
attempt to execute 17 GOSUBs before using a RETURN, you earn a "GOSUB
Stack Overflow" error, and your script aborts.
As RETURN commands are encountered, the script processor returns to the
position of the *last* GOSUB command. Example (see the comments [;] for
an explantion):
GOSUB Subroutine1
exit
Subroutine1:
print "Executing subroutine 1."
GOSUB Subroutine2
RETURN ;would return to the EXIT below 'GOSUB Subroutine1' above
Subroutine2:
print "Executing subroutine 2."
RETURN ;would return to the RETURN below 'GOSUB Subroutine2' above
And again, it makes no difference whether the RETURN commands are
actually 'in' the subroutine. If 'GOTO Subroutine2' was used above,
instead of GOSUB, the RETURN found in Subroutine2 would cause a return to
the EXIT command below the call to Subroutine1. Just picture the
Intellicomm v2.01 SCRTUTOR.DOC 31
positions of your GOSUB commands being placed in a hat one after the
other (you can't get to the previous position without removing the last
one) with the RETURN command simply removing those positions (last in,
first out) and you'll have no surprises.
Note also that there is no difference between a GOTO label, and a GOSUB
label. You can GOTO a subroutine label if you like -- but be cautioned
that if a RETURN is found -- and no GOSUB was executed previously (i.e.
there are no saved positions on the subroutine stack), it causes the
script to end.
3.4 Creating a Subroutine Template
If you find yourself writing the same subroutines over and over again in
your scripts, you should consider creating one or more subroutine
"template" files, which contain your common subroutines. You can call
template files anything you like: TEMPLATE.SCR is a good choice. Then,
whenever you begin writing a new script, simply hilight TEMPLATE.SCR,
select "Copy" from the Script Manager, and copy it to another filename
(the name of the script you wish to create). Then begin your script by
"Edit"ing the newly copied template.
All the subroutines will be in place, and you can begin coding
immediately. This practice will also help you to standardize the names
of your subroutines, so that you're always using the same subroutine
label for a specific function (PressAKey, instead of Press_a_Key,
PressKey, etc., which could get confusing after a time). It will also
give you more reliable scripts, since you'll be using (hopefully) tested
subroutines instead of writing from scratch, where you might make a
mistake.
Intellicomm v2.01 SCRTUTOR.DOC 32
4. SCRIPT VARIABLES IN DETAIL
4.1 Why Would I Want to Use Variables?
Variables are what allow computer programs (and scripts) to "know"
something. They give your scripts a memory so that they can remember
things, and thus become smarter. If an error occurs, you can have the
script remember the error or count the occurrences of errors through the
use of variables. If you need the user to enter something (a phone
number or the like), you can have your script store what the user enters
in a variable, and thus your script can 'remember' what the user entered.
Without variables, scripts have no way of remembering anything at all.
Variables are also REQUIRED by several of the script commands. If you
want to do any math (add, subtract, multiply, divide numbers) get
keyboard input from the user, read text files on-disk, or do any sort of
string 'handling' (joining two pieces of text together, extracting
characters from a line of text, reading text from the video screen, etc.)
then you must use variables to store the results.
There are six types of variables in the Icom script language: 'User-
Defined Variables', which you create yourself in your scripts, 'Global
Variables' which are similar to the User-Defined Variables but exist for
the duration of the Icom session, 'System Variables', which are similar
to the Global Variables, but hold information about your computer system
and Intellicomm, BIF Variables, which hold all the current BIF
information, Main Setup Variables, which hold all the current INI
(INItialization or main setup) information, and Permanent Variables,
which are stored on-disk.
Each type has a different purpose but the reason for variables of all
types is to hold information and thus to avoid specifying CONSTANT data
in your script commands. Anywhere you can specify constant data in a
script parameter ('PRINT "John Smith"' prints constant data), you can use
a variable ('PRINT myvariable' prints the CONTENTS of the variable
'myvariable'). Several examples are given below to show you how to make
effective use of all types of variables.
4.2 Where you CAN'T use Variables
You cannot use variables in place of script GOTO/GOSUB labels. If you
try, you'll get a 'Label not found' error, or worse, Icom will jump to a
'label:' you didn't expect it to jump to. 'GOTO myvariable' (if
myvariable was a variable) would cause Icom to look for a LABEL called
'myvariable:'. Thus, you cannot use variable names when a GOTO/GOSUB
label is expected.
4.3 User-Defined Variables
User-defined variables are variables you create yourself. All you do to
create a variable is think up a name for it, then use the VARIABLE
command to define it in your script:
Intellicomm v2.01 SCRTUTOR.DOC 33
VARIABLE myvariable ;define a variable called 'myvariable'
You can also 'assign' something to the variable when you define it by
following the variable name with whatever you want to assign to it:
VARIABLE myvariable "Please hold this text in myvariable"
Once the variable is defined, you can use it in any script command that
accepts parameters. If you printed this variable above:
PRINT myvariable
...then PRINT would display this on the screen:
Please hold this text in myvariable
If you intend to upload your script to a BBS for public distribution,
User-Defined Variables also make it easier for users of your script to
fill in any required information. Instead of forcing the user to weed
through the entire script and make prompt or response changes, you can
define all the necessary information using variables at the TOP of the
script where they can easily be found:
VARIABLE YourName "John Smith" ;<-- Put your name here
VARIABLE Password "password" ;<-- Put your password here
Placing variables at the TOP of the script makes it much easier for a
novice to edit, and it also makes it easier for YOU to maintain your own
scripts than it would be to have to weed through the script and change
WHEN or SEND commands.
4.4 User-Defined Variable Rules
User-Defined Variables cannot be used in script commands until you define
the variable (give it a name) using the VARIABLE command. And you must
define each variable with the VARIABLE command ONCE and only once (per
script ... you can use the same variable name in another script). After
you've defined it, you can change the contents of the variable using
ASSIGN and several other commands, but the VARIABLE command should be
used only once per variable. If you do this:
variable x 0 ;define variable 'x', store 0 in it
variable x 10 ;define variable 'x', store 10 in it
Icom will abort the script with a 'Variable exists' error when it finds
the second VARIABLE command. What you would do instead of using the
variable command twice is this:
variable x 0 ;define variable x, store 0 in it
...
assign x 10 ;later in the script if you want to change the
; value, you use ASSIGN. This example would store the
; number 10 in variable x.
Intellicomm v2.01 SCRTUTOR.DOC 34
ASSIGN can be used to assign either text or numers to a variable.
You cannot use a script COMMAND as a variable name:
variable waitfor ;duplicates the WAITFOR command
If you do this inadvertently, the script will abort with the error:
'variable name duplicates a script COMMAND'. At that point you simply
think up a new name (add a 1 to the end of the variable name VARIABLE
WAITFOR1 or the like) and you're set.
Case is NOT significant in variable names:
variable myvariable ;lowercase
assign MYVARIABLE 10 ;uppercase... they're both the same to Icom
You can store anything in a variable; numbers, text, symbols, graphics
characters ... anything you like (including control characters).
However, the maximum length for data stored in each user-defined variable
is 256 characters.
User-defined variable names can also be (almost) any name you like. If
you were counting something you might use this:
variable count 0 ;define variable 'count', assign 0 to it
If you were storing your name, you might use this:
variable MyName "John Smith"
You CANNOT do this, however:
variable My Name "John Smith" ;note the space in 'My Name'
Spaces aren't allowed in variable names. You can use only letters,
numbers (see below), and underscores (_):
variable My_Name 10 "John Smith" ;this is fine with an underscore
Further, you cannot BEGIN a variable name with a number: it must start
with a non-numeric character. You can use numbers after the first
character, but the very first character of a variable name must be either
a letter or an underscore. These are all valid variable names:
VARIABLE count1 ;numbers are okay after the 1st character
VARIABLE c1
VARIABLE _1
but you cannot use this:
VARIABLE 1count ;cannot start a variable name with a number
Icom stores only the first *30* characters of variable names you define
Intellicomm v2.01 SCRTUTOR.DOC 35
with the VARIABLE command in its internal table of variable names, and
again only checks the first 30 characters when you specify a variable in
a script command parameter. These two variable names are the exact same
names to Intellicomm:
VARIABLE this_is_very_long_variable_name_1
VARIABLE this_is_very_long_variable_name_2
The cutoff point, 30 characters, leaves Icom with:
VARIABLE this_is_very_long_variable_nam
for BOTH of the above variable names. The above would abort the script
with a 'Variable exists' error, since you can't define the same variable
twice in the same script.
4.5 Why All the Rules?
The 30 character limit to variable names is common in programming and
script languages (some allow more, many allow less) and the main reason
is to conserve memory. Every name of every variable you create has to be
stored in memory in an internal table, so the script processor can find
the data when you specify the variable name in a script command. 30
characters is a reasonable trade-off between memory conservation and
functionality.
Intellicomm also has to have some way of knowing whether you're
specifying a constant number (1 for example is a constant number), or
constant text (surrounded by single or double quotes), or a variable.
The rules outlined above are how it does it. If a parameter starts with
a single or double quote, the script processor knows you're specifying
constant text. If a parameter starts with a number it assumes you're
specifying a constant number. If a parameter has neither a quote nor 0-9
as the first character, the script processor assumes you're specifying a
variable name and will check its internal tables of variable names to
locate the proper data. Most computer languages use these same rules to
separate constants from variables, and numbers from text (strings).
Further, other symbols such as +, -, =, <, >, /, *, %, ^, &, |, $, !, [],
(), etc., may all be used for various purposes in future expansions of
Icom's script language (or are already used), so for maximum
compatibility with future releases you can use only a-z, A-Z, _, and 0-9
(subject to the rule above about numbers) in your variable names. By
VARIABLE NAMES this doesn't mean the text you specify between the quotes
that is assigned to variables. Between quotes you can use any characters
you like (almost... you can't use a real Line Feed or it would put the
text on another line... use "^J" [Ctrl-J] for LF).
Variable names themselves must never be put in quotes, or Icom will think
you're specifying literal text and won't check its table of variable
names. If you did this:
PRINT "myvariable"
Intellicomm v2.01 SCRTUTOR.DOC 36
the text "myvariable" (minus the quotes) would simply be printed to the
screen. To print the contents of a variable called myvariable, do not
use quotes:
PRINT myvariable ;this accesses the CONTENTS of variable 'myvariable'
4.6 Using Variables
You can use variables in any of Intellicomm's script commands that accept
parameters, instead of specifying the parameters literally. You wouldn't
want to have to write five different scripts that all do the same thing,
but simply account for slightly different conditions such as a different
phone number; so instead of changing one script line then duplicating the
entire script, you just use variables, then change the variables, and the
script works as is.
This command could dial only "Joe's BBS":
dial "Joe's BBS"
While this script, using variables, could dial any BBS:
variable BBSName ;define variable 'BBSName'
boxgets BBSName 20 "Dialing a BBS" "Enter the name of a BBS to dial"
dial BBSName
We used a new command here that you haven't seen before, called BOXGETS
and what it would do (given the parameters above) is accept a 20
character string (the 20 following command) in a box that looked like
this, but with graphics characters instead of text:
+=| Dialing a BBS |==================+
| |
| Enter the name of a BBS to dial |
| |
| > |
| |
+====================================+
Whatever was entered in the box would be stored in the variable 'BBSName'
(the first parameter in the BOXGETS command). You then just
DIAL BBSName
and whatever name was entered in the box would be dialed.
When you check the detailed command summary for BOXGETS you'll find all
the details, such as how to check to see whether the user pressed the
[Esc] key, how to look for a blank string, etc. You should also look up
the DIAL command, since it can be used to simply display the entire BBS
Directory, allowing the user to Tag/Dial BBS's... which may be more
desirable than what was done above.
Intellicomm v2.01 SCRTUTOR.DOC 37
When you apply the same concept we used with the DIAL command above to
WHEN, WAITFOR, and all the other script commands, using variables instead
of specifying text directly, it allows you to write much more flexible
scripts (and you may see more clearly how Icom itself works... it simply
loads the BIF information into its variables and executes the same code
no matter what BBS is being called).
You needn't get variable information from the keyboard; you can get it
from a BIF, the Icom main setup data, from a text file on-disk which you
can create yourself manually or with other script commands, or you can
create variable data on-the-fly based on day of week ($DOW), month
($MONTH), day of month ($DAY), time ($HOUR, $MIN, $SEC), etc. For
example:
variable BBS_to_call
if $DOW = 1 assign BBS_to_call "Joe's BBS"
if $DOW = 2 assign BBS_to_call "Canada Remote System"
if $DOW = 3 assign BBS_to_call "Sound Advice"
; ...etc.
DIAL BBS_to_call
The first IF statemtent means 'if the day-of-week is equal to Sunday (1),
put the text Joe's BBS in variable BBS_to_call'. If $DOW is equal to 2
(Monday), we ASSIGN another BBS name. I.e. we are ASSIGNing data to the
variable on-the-fly, according to the day of the week.
You don't have to use variables for the time being, and are actually
better off sticking with literal text and numbers until you get the hang
of things. In the beginning the main goal is to write scripts that WORK.
Writing flexible scripts with variables is something you can look into
when you're comfortable with the language and have written a few working
scripts without variables. A good way to get practice is to write your
scripts with constants first, get them working, and THEN to go and
replace one or two of the constants with variables to get the hang of it.
4.7 The Life of a Variable
User-Defined Variables have a fleeting life most days. They live (exist
in memory) only from the time they are defined with the VARIABLE
commmand, and they die (do not exist in memory) when the script ends.
Their use is also limited to the current script. If you created one
script that did this:
variable I_Need_This_Number_Elsewhere 10
...then created another script and executed that second script using the
SCRIPT command and tried this in the SECOND script:
print I_Need_This_Number_Elsewhere
then Icom would abort the second script with the error: "Undefined
Variable; I_Need_This_Number_Elsewhere". Variables defined with the
VARIABLE command are accessable only from the script in which they were
Intellicomm v2.01 SCRTUTOR.DOC 38
created, and only from the point of creation onwards. You can't do this
either:
print x ;don't attempt to use here
variable x 10 ; when it hasn't been defined yet
Before using a variable the FIRST thing you must do is define it with the
VARIABLE command. Otherwise Icom won't have the name of the variable in
its internal table of variable names, and also won't have allocated any
memory space to store the variable data. Variables can be used 'above'
the VARIABLE command if you can manage to get the variable defined first.
You can do this for example:
gosub define_variables ;jumps to 'define_variables:', then returns
print x ; <-- to this line when the subroutine 'returns'
exit ;exit before we hit 'define_variables' again
define_variables: ;beginning of subroutine 'define_variables'
variable x 10 ;define variable 'x' for use above
return ;end of subroutine 'define_variables'
Having variables disappear when a script ends, and only allowing the
current script to access them may seem like a limitation at first, but
both are actually useful features, and both were done intentionally. If
variables DID exist after the script ended, you'd be tieing up memory on
the computer, possibly never to be used again during that Icom session.
By having variables 'die' when the script ends, all the memory they were
using is freed up.
The other feature; limiting the use of variables to the current script is
also very useful. If you had one script that used a variable called 'x',
and you had some important data in 'x' and then called ANOTHER script
(using the SCRIPT command, which runs a script from a script) that used
the same variable name... you may lose your original value in 'x' if the
second script also had an 'x' and modified the value. If variables
didn't work this way you'd constantly have to worry about conflicting
variable names in other scripts, that might change your original data...
and tracking down such problems would not be easy.
4.8 Global Variables
For the cases where you DO want certain data to be available from one
script to another (for inter-script communication and the like) an
'array' of 'global variables' has been provided. Did your face go pale?
I know it sounds rather complicated, but the Global Variables can be very
useful and are well worth knowing about.
The 'global' script variables are available from the second Icom starts
until you exit the program. I.e. they hold their data from one job to
the next, from one script to the next (with some exceptions... see below)
-- for as long as Icom itself remains in memory. The global variable
array is called GlobalStr (Str for 'String'... that means it can hold
text OR numbers, as with the User-Defined Variables).
Intellicomm v2.01 SCRTUTOR.DOC 39
The term 'array' just means that there are more than one of them: there
are 10 in fact. The first is GlobalStr[0], the second is GlobalStr[1],
the third is GlobalStr[2], etc., all the way up to GlobalStr[9] (0-9 = 10
variables). The GlobalStr variables are each 64 characters long, so you
can't store anything longer than 64 characters in any one of the 10
GlobalStr array members. If you attempt to store something longer than
64 characters, the data will be truncated (cut) at 64 characters by the
script processor.
Now you're probably wondering about those square brackets, and may be
wondering why it's not just GlobalStr1, GlobalStr2, etc. The reason for
that, and the reason for arrays in general in all programming languages,
is so that you can use ANOTHER variable to specify WHICH of the global
variables you want to access. Watch this carefully:
variable Name_Data 5 ;define Name_Data, assign 5 to it
printnc "Enter your name: " ;print with no carriage return
gets GlobalStr[Name_Data] 64 ;get user name, store in GlobalStr[5]
print GlobalStr[Name_Data] ;print GlobalStr[5]
(The 64 following the GETS command specifys the maximum number of
characters to get. Remember; each GlobalStr member can hold a maximum of
64 characters.)
Instead of specifying a constant number in the square brackets of
GlobalStr, we used the variable 'Name_Data', which was holding the value
of 5. So, GlobalStr[5] gets the data, since 5 is what 'Name_Data' was
holding. Later in your script you might not remember that you stored the
user's name in GlobalStr[5], so it's much easier to just assign the value
of 5 to 'Name_Data', and then to use GlobalStr[Name_Data]. It's easier
to work with and remember than GlobalStr5 would be, were it not an array.
One thing you CANNOT do, at least in this version of the Icom script
language is this:
GETS GlobalStr[GlobalStr[5]] 64
In real programming languages this sort of thing IS legal... and all it
would do is access the contents of GlobalStr[5] to get the array offset.
If GlobalStr[5] was holding the value 1, a real programming language
would store the result of the GETS command above in GlobalStr[1].
However once we get into things like that, this becomes possible:
GlobalStr[GlobalStr[GlobalStr[GlobalStr[2]]]]
...and that's a little too involved to program the script interpreter to
handle at present. I may get into it in the future, but didn't have time
for v2, so you MUST use either a constant number:
GlobalStr[0] to GlobalStr[9]
Or a user-defined variable (defined with the VARIABLE command) that is
HOLDING a number from 0-9:
Intellicomm v2.01 SCRTUTOR.DOC 40
variable globalstr_offset 9 ;assign 9 to globalstr_offset
GlobalStr[globalstr_offset] ;GlobalStr[9]
You MUST NOT USE SPACES anywhere from GlobalStr[ to the closing ]. These
are programming errors:
GlobalStr [offset] ;space between GlobalStr and [
GlobalStr[ offset ] ;spaces in the brackets
GlobalStr[ 5 ] ;spaces in the brackets
It must be kept together as a single unit, with no spaces, and this is
the correct way to specify the three example errors above:
GlobalStr[offset]
GlobalStr[offset]
GlobalStr[5]
4.9 Using Global Variables
Until you become more familiar with the other script commands and
concepts it's difficult to demonstrate any advanced use of these
variables. But their purpose is threefold: (1) to allow inter-script
communication (access to the same data by multiple scripts... which isn't
possible with the User-Defined Variables), (2) to allow 'command line
parameters' to be passed to scripts from DOS (the /scr: command line
switch) BIFs, Custom Commands, and other scripts (the parameters are
stored in the GlobalStr array), and (3) basic array usage, which, in real
programming languages is indespensible.
The main idea behind an array is to use ANOTHER variable as an index to
access members of the array. You can start the index variable
('Name_Data' in the example above was the index variable) at 0, then add
one to the index variable whenever you need another global variable:
variable gindex 0 ;start at 0
assign GlobalStr[gindex] "Some data" ;goes in GlobalStr[0]
inc gindex ;INCrement: gindex = gindex + 1
; ... later in your script
assign GlobalStr[gindex] "More data" ;goes in GlobalStr[1]
inc gindex ;gindex now equals 2... ready
; for the NEXT bit of data
Or you can use a 'loop' to clear all the variables in the array, or
combined with other functions can quickly copy data from various sources
into the array. A 'loop' simply means that Icom will execute the
commands between WHILE and ENDWHILE (below) as long as the condition
specified in the WHILE remains true. I.e. as long as the value in
'offset' is less than or equal to the number 9 (9 being the GlobalStr
array limit... GlobalStr[9] is the end of the array). Since we start
with 'offset' at 0, and add one to it at the bottom of the loop, the
Intellicomm v2.01 SCRTUTOR.DOC 41
commands in the loop will be executed 10 times each. More details on
loops can be found in the WHILE Detailed Command Summary. Here's a quick
example:
variable offset 0 ;define variable 'offset', set to 0
while offset <= 9 ;while offset is less than or equal to 9
printnc "Enter item " ;These five lines in the loop will be
printnc offset ; executed UNTIL 'offset' is greater than
printnc ": " ; 9. When 'offset' is 10, we exit the
gets GlobalStr[offset] 40 ; loop and continue below ENDWHILE
inc offset ;increment: offset = offset + 1
endwhile ;end of the 'loop'
print "Here is what you entered:"
assign offset 0 ;It's very important to reset 'offset' to 0
while offset <= 9 ; or else we wouldn't enter this 'loop' at
print GlobalStr[offset] ; all. Remember it's now holding the number
inc offset ; *10* due to the loop above, and 10 is NOT
endwhile ; less than or equal to nine.
Loops and arrays were invented to avoid having to do this:
printnc "Enter item 0: "
gets GlobalStr[0] 40
printnc "Enter item 1: "
gets GlobalStr[1] 40
printnc "Enter item 2: "
gets GlobalStr[2] 40
;this would continue right up to GlobalStr[9] to accomplish
; the same thing the FIRST loop above did.
print "Here is what you entered:"
print GlobalStr[0]
print GlobalStr[1]
print GlobalStr[2]
;this would also continue right up to GlobalStr[9] to accomplish
; what the SECOND loop above did.
Of course, the same principle can be applied to ANY script command (any
commmand that takes a parameter), and GlobalStr isn't limited to GETS and
PRINT and ASSIGN. You can use this global array variable anywhere your
imagination takes you, with any script command that takes parameters.
4.10 Passing Parameters to Scripts
When parameters are passed to a script, either from the BIF:
| Your Logon Name> @DEMO parm1 parm2 Name . . . . . . st Name? « |
^^^^^^^^^^^^^^^^^
or from a Custom Command:
| 12 Custom Command/Run script | CC: @DEMO parm1 parm2 ... |
^^^^^^^^^^^^^^^^^
Intellicomm v2.01 SCRTUTOR.DOC 42
or from a script via the SCRIPT command:
SCRIPT "DEMO.SCR" "parm1" "parm2" variable_parm
or from the ICOM.EXE /scr: command line option:
ICOM.EXE /scr:"DEMO.SCR parm1 parm2"
the first parameter is stored in GlobalStr[1], the second in
GlobalStr[2], then GlobalStr[3], and so on. The quotes are needed when
specifying parameters in the /scr: command line option (see above) to
keep it all together. Otherwise DOS separates the parameters due to the
spaces between parameters, and they look like separate 'switches' to
Icom.
Note that when using the SCRIPT command, you can pass VARIABLES instead
of literal strings (variable_parm above assumes the script had a variable
called variable_parm), which simply has the effect of copying the
variable data into the GlobalStr array. As mentioned previously the
regular user-defined variables are available ONLY within the script
they're defined in. By using variables as parameters in a SCRIPT
command, the called script could then access the data by looking in
GlobalStr. You can also just copy the data explicitly, if you like,
instead of passing parameters:
assign GlobalStr[1] "parm 1" ;copy 'parm 1' into GlobalStr[1]
assign GlobalStr[2] "parm 2"
assign GlobalStr[3] variable_parm
SCRIPT "DEMO.SCR"
The above has the same effect as the previous example, which specified
parameters directly in the SCRIPT command.
NOTE: When passing parameters from a BIF or Custom Command, Icom
separates the parameters by looking for space " " characters. As soon as
a space is found, the data following the space goes in the next GlobalStr
member. If you wish to pass a single parameter which CONTAINS a space,
you must put the parameter in quotes (similar to DOS command line
options):
| 12 Custom Command/Run script | CC: @DEMO "parm 1" "parm 2" |
^^^^^^^^^^^^^^^^^^^^^^^
The two parameters specified above would go in GlobalStr[1] and
GlobalStr[2] (the quotes are stripped before storing the parameter(s) in
GlobalStr).
Alternatively you could just put quotes around ALL the parameters (to
have them all stored in GlobalStr[1]), then use the STRITEM command to
get the individual parameters yourself:
| 12 Custom Command/Run script | CC: @DEMO "parm1 parm2" |
^^^^^^^^^^^^^^^^^^^
From inside the script you'd do this to get the individual parameters:
Intellicomm v2.01 SCRTUTOR.DOC 43
variable parm1
variable parm2
;GlobalStr[1] would be holding "parm1 parm2" (no quotes) given the
; Custom Command example above
STRITEM parm1 GlobalStr[1] 1 ;get item 1 from GlobalStr[1], put in parm1
STRITEM parm2 GlobalStr[1] 2 ;get item 2 from GlobalStr[1], put in parm2
Please see the Detailed Command Summary for more information on STRITEM.
4.11 Checking the Number of Passed Parameters
If you were paying close attention earlier, you might be wondering why
the parameters don't start at GlobalStr[0] ... since GlobalStr[0] is the
FIRST variable in the array ... not GlobalStr[1]. The reason for that is
to allow you to quickly check how many (if any) parameters were passed to
your script. The script processor stores the total number of parameters
(a number from 0 to 9) passed to your script in GlobalStr[0]. So, if
your script REQUIRED 3 parameters to be passed to it, and they weren't
all specified, you'd know about it quickly:
if GlobalStr[0] <> 3 ;if GlobalStr[0] is not equal to 3
msgwind "Hey, you were supposed to specify 3 parameters!"
return
endif
print "Thanks for the 3 parameters. You specified:"
print GlobalStr[1]
print GlobalStr[2]
print GlobalStr[3]
return
It's as easy as 0-1-2 (or 1-2-3, whichever you prefer). Of course, this
GlobalStr[0] business is something you'll have to keep in mind (a) if
you're storing anything important in GlobalStr[0] and (b) if you use the
SCRIPT command to call another script from your existing script. I.e. if
anything important is stored in GlobalStr[0] (your own data... Icom
doesn't care what's in GlobalStr[0]) you'll lose it if you execute a
SCRIPT command from within the current script. You can overcome any
problems by doing this:
variable some_variable
assign GlobalStr[0] "Important data" ;holding something important
... ;meanwhile, later in the script...
assign some_variable GlobalStr[0] ;save GlobalStr[0]
script "ANOTHER.SCR" ;run ANOTHER.SCR
assign GlobalStr[0] some_variable ;restore GlobalStr[0]
Note that even though you didn't PASS any parameters to ANOTHER.SCR, the
script processor would still store the number 0 in GlobalStr[0], to tell
ANOTHER.SCR that no parameters were passed. Of course, if you DID pass
parameters to ANOTHER.SCR, then GlobalStr[1] and possibly GlobalStr[2],
Intellicomm v2.01 SCRTUTOR.DOC 44
etc., would also be lost, depending on how many parameters you specified.
If you pass 9 parameters to a script then the entire GlobalStr array is
overwritten with your parameters.
Now that you know this though, it should be no problem to simply save any
important data in GlobalStr before executing the SCRIPT command, then to
restore the information after the SCRIPT command as illustrated above.
TIP: If you DO have something important to hang onto (and it must be
'globally' available to other scripts), you're much better off storing it
in GlobalStr[9] than you are storing it in GlobalStr[0]. It's not likely
that 9 parameters would ever be passed to a script, so keep your
important global data at the high end of GlobalStr, and keep the lower
portion open for passing parameters. Data stored in GlobalStr[9],
GlobalStr[8] probably all the way down to GlobalStr[5] would most likely
remain untouched throughout the entire Intellicomm session, from one job
to the next, from one script to the next, etc.
If the data won't be needed outside the current script though, there's no
need to use GlobalStr at all... you can just use the regular variables
(defined with the VARIABLE command), which are protected when you run
another script.
4.12 System Variables
You can also obtain various system information to use with script
commands and to put in your variables, such as the system date and time,
the current up/download directories and protocols, and lots of other
useful information. System variables begin with a dollar sign ($) and
can be used in any Icom script command that accepts parameters:
print $DATE
This command prints today's date to the screen (in the date format
defined by the user in the Icom main setup... usually MM/DD/YY). System
Variables increase flexibility and allow you to write scripts for use by
others, since you needn't use constant data. For example, DOWNLOAD "Z"
was used to demonstrate the download command earlier, but that's
inflexible and forces Zmodem. With Icom, everyone defines the upload and
download protocols they prefer to use in their BIFs, so instead of using
"Z", you can use a System Variable:
download $DL_PROTOCOL
Whatever protocol the user had defined as the file download protocol (in
whatever BIF was currently loaded) would then be called to carry out the
download -- even if it was an external protocol. You can also use System
Variables to *check* settings if need be:
if $DL_PROTOCOL <> "Z" ;not Zmodem?
print "Sorry, this script requires use of Zmodem. Please set your"
pause "BIF up to use Zmodem and re-try. Press a key..."
exit
endif
Intellicomm v2.01 SCRTUTOR.DOC 45
You could also just ASSIGN $DL_PROTOCOL "Z" to have your script change
it, instead of aborting the script... but if you do that, make sure you
save the ORIGINAL value of $DL_PROTOCOL in a user-defined variable, and
restore the value before your script ends.
Please see the System Variable Appendix and/or the SCRIPT COMMANDS AT A
GLANCE section of SCRIPT.DOC for a listing of all the System Variables.
4.13 BIF Variables
BIF Variables start with an asterisk (*), are then followed by the BIF
'section' in square brackets ([G] = General screen, [L] = Logon screen,
etc) then the "tag" of the BIF item. Example:
WHEN *[L]namp SEND *[L]namc ;BIF Logon name prompt/command (response)
You can access (and change, if necessary) any of the BIF variables in
this way. All the BIF sections and tags are documented in SCRIPT.DOC in
the "BIF Variables" section.
NOTE: Until Icom dials or connects to a BBS, and after it disconnects,
the BIF data is unpredictable and could be that of any BBS (check the
$BIF_NAME System Variable to get the filename of the currently loaded
BIF, if any). You can use the LOADBIF command to load a specific BIF
into memory if you need specific BIF data.
4.14 Main Setup and Environment Variables
You can also access (and change) any of the Icom main setup by specifying
an asterisk (*) followed by a main setup "tag". Example:
ASSIGN *dtime 60 ;60 second 'Timeout' when dialing
Each variable holds a specific piece of information, in a specific FORMAT
and you shouldn't use any of these variables before reading the "Main
Setup Variables" section in SCRIPT.DOC.
Note that these settings are stored on-disk in ICOM.INI by default, which
is loaded into memory (the Main Setup Variables) when Icom initializes.
If necessary you load and save various .INI files using the LOADINI and
SAVEINI commands.
4.15 Environment Variables
You can also access variables in the ICOM.EXE program 'environment'
(where the DOS PATH is stored, and any other environment variables the
user had defined with the DOS 'SET' command) using GETENV / SETENV. Each
of these commands is described in the Detailed Command Summary section of
SCRIPT.DOC.
Intellicomm v2.01 SCRTUTOR.DOC 46
5. PERMANENT VARIABLES: INTRODUCTION TO FILE INPUT/OUTPUT
Permanent variables exist even after the computer is turned off. They're
not stored in memory as the variables discussed above are; they're stored
on-disk. Through the use of the script file Input/Output (file I/O)
commands you can have your scripts 'remember' things forever... or at
least until disk failure. Likely candidates to become "permanent"
variables are any user-configurable settings your script must remember
from one run to the next.
You don't have to know anything about disks, or how DOS works with files
to use the file I/O commands. If you can PRINT data on your monitor with
a script, then you're just a couple of steps away from doing the
equivalent of PRINTing data to a file using the file I/O commands.
If you're nervous about using the disk, and think you might screw your
whole hard drive up accidentally, relax. Using the script file I/O
commands is no less "dangerous" than starting up your word processor,
typing a letter, and saving it on-disk using the proper filename
('proper' meaning that you don't inadvertently type the wrong filename
frequently and overwrite imporant files by accident). If you can do that
without worrying about losing important data on your disks, then you're
fully qualified to use the script file I/O commands: All you need to know
about files, as with your Text Editor, is the D:\PATH\FILENAME.EXT of the
file. They do nothing you can't already do from your word processor or
text editor (except to automate the task of creating and maintaining the
files... and to allow you to do it from a script instead of having to
start up the Text Editor and do it manually).
DOS takes care of all the nitty grittys as far as disk use goes (the
script file I/O commands simply pass intructions to DOS, after verifying
them) and DOS knows what it's doing. It won't *let* you screw up and
unintentionally overwrite other files on your disk, no matter how hard
you try. To overwrite existing data on-disk, you must be quite specific
about your intentions. You can't open one file, and inadvertently write
to another ... no more than you can load a specific file into your Text
Editor and inadvertently end up overwriting some other file on-disk by
"typing too much". It just doesn't happen. If you add to a file (any
file) DOS uses *free* disk space to save the new data, and it will never
overwrite the data in another file to expand an existing file: That's
what "Disk Full" messages are for... and you'll get a "Disk Full" error
using the File I/O commands as well, if there's no *free* space on disk,
and you attempt to add new data to a file.
And luckily, no rocket science is required: (1) You first open the file
you're interested in, by specifying its filename; and this is why it was
said above that you cannot UNINTENTIONALLY overwrite files on your hard
drive ... unless you're a klutz with filenames; (2) you then read or
write to the file line by line, transferring the information from the
file into your variables (to memory), or from variables/constants into
the file; (3) then you close the file. It's simple! Here's a quick
example:
Intellicomm v2.01 SCRTUTOR.DOC 47
variable f1 ;variable used as the file "handle". You'll
; note below that FOPEN, FPUTS, and FCLOSE
; all use this variable
FOPEN f1 "TEST.DAT" "w" ;open TEST.DAT for "w"riting; overwrites any
; files on-disk of the same name
FPUTS f1 "John Smith" ;write this text to the file, following each
FPUTS f1 "123 Anystreet" ; fputs with a Carriage Return/Line Feed. To
FPUTS f1 "Anytown, Ont." ; write WITHOUT adding a CR/LF, use fputsnc
FCLOSE f1 ;close the file
If you executed the above script, you'd end up with a text file on-disk
called TEST.DAT (in the current directory, whatever that happened to be)
that contained the following:
John Smith
123 Anystreet
Anytown, Ont.
The only 'danger' involved (when opening a file for "w"riting, as we did
above) is in specifying the proper filename... just as you make sure to
specify the proper filename when you save a file on-disk from your word
processor. And just as in your word processor, if you don't specify a
drive and path (D:\PATH\) with the FILENAME.EXT, the file is created in
the current directory. By ALWAYS specifying the full
D:\PATH\FILENAME.EXT of the files you FOPEN, you'll never have to worry
about inadvertently overwriting useful files. In many of the examples
below, the D:\PATH\ is omitted for brevity, but in a real script you must
ALWAYS either use a CHDIR command, or specify the full
D:\PATH\FILENAME.EXT. Either of these are fine:
CHDIR "C:\SOMEDIR"
FOPEN f1 "SOMEFILE.EXT" "w"
FOPEN f1 "C:\SOMEDIR\SOMEFILE.EXT" "w"
The latter is the safest, since a CHDIR command will fail if the
directory doesn't exist... and were that the case, the first FOPEN would
simply create SOMEFILE.EXT in the current directory, *overwriting* any
file of the same name that happened to exist in whatever directory you
were in at the time. Once you FOPEN a file for "w"riting, you
immediately destroy the contents of any file of the same name. So make
sure you get the filename right when you FOPEN for "w"riting!
5.1 FOPEN 'Modes'
You can FOPEN a file for reading (no writing... the file must exist),
writing (no reading... the file will be created and will overwrite any
file of the same name) or 'appending' (add to the end of an existing
file, or create the file if it doesn't exist). And once you master file
I/O, you can even open a file for reading AND writing, if the need arises
(more on this later). The "w" above in the FOPEN command (after the
filename) specifies writing only, this mode overwrites any file of the
same name that exists in the current directory; "r" specifies reading
Intellicomm v2.01 SCRTUTOR.DOC 48
only, and "a" specifies appending (create or add to).
5.2 The File Handle
The variable 'f1' is specified in all of the file commands to identify
WHICH file you want to operate on. Several files can be FOPENed at the
same time -- usually for the purpose of reading data from one file and
writing it to another -- and it would be tedious to have to specify the
filename every time:
FOPEN "test.dat" "w"
FPUTS "test.dat" "John Smith"
...
FCLOSE "test.dat"
And it would be even more tedious were you operating on a file called
"C:\MYDIR\SUBDIR1\SUBDIR2\SOMEFILE.TXT". So instead of forcing you to
specify the filename every time you operate on an open file, FOPEN takes
a variable and then lets you use that variable name from then on (until
after you FCLOSE the file) to 'refer' to the filename you specified in
the FOPEN command.
The variable name you choose for the file handle is up to you, and 'f1'
was just an example. You could call the file handle variable 'test_dat'
(or any other legal variable name) if you wanted to.
Since you can FOPEN several files at a time, whenever you FPUTS (write
to), or FCLOSE a file, the script processor has to know *which* file you
want to operate on. FOPEN stores a number called a file "handle" in the
variable you specify before the filename, and by specifying that same
variable in all the file commands later, the script processor always
knows which open file you want to deal with. Simple!
If you ran the example script above, you could then load the file
TEST.DAT into your Text Editor and read it or modify it just like any
other text file. Or, at some later point on some later day you could
load the information and use it in a script:
variable Name ;define some variables
variable Address
variable City
variable f
FOPEN f "test.dat" "r" ;open for "r"eading
FGETS Name f ;read first line, store in 'Name'
print Name ; this prints John Smith
FGETS Address f ;read next line, store in 'Address'
print Address ; this prints 123 Anystreet
FGETS City f ;read next line, store in 'City'
print City ; this prints Anytown, Ont.
FCLOSE f
FGETS reads data from a file, and stores it in a variable (or variables
in this case; Name, Address, and City). Note that FGETS takes the file
Intellicomm v2.01 SCRTUTOR.DOC 49
handle (the variable 'f' holds the file handle) as the SECOND parameter.
As with all script commands, the variable you're ASSIGNING data to is
always the first parameter specified. FPUTS takes the file number as the
first parameter because it doesn't assign anything to the variable, and
also to allow you to FPUTS more than one item:
variable some_text "some text"
FPUTS f "This is " some_text " to FPUTS"
After looking at the prior example, you may be wondering how FGETS knows
what to store in which variable... How it knows to put the name in
'Name', the address in 'Address' and so on. But the fact is it doesn't:
we do. All FGETS does is to read the file line by line. The first call
to FGETS reads the first line... the second reads the second line, the
third reads the third line. WE know that the first line contains the
Name, and the second line contains the Address, etc., since we created
the data file earlier in the first example. So we knew enough to call
the variables 'Name', 'Address' and 'City' since that's what's in the
data file. You 'could' do this, though it wouldn't make much sense:
FOPEN f "test.dat" "r" ;open for reading
FGETS f City ;read first line, store in 'City'
print City ; <-- this still prints John Smith
FGETS f Name ;read next line, store in 'Name'
print Name ; <-- this still prints 123 Anystreet
FGETS f Address ;read next line, store in 'Address'
print Address ; <-- this still prints Anytown, Ont.
FCLOSE f
This serves to prove that the names of variables are irrelevant to the
script processor... You can call a variable anything you like, and you
can give ANY variable to ANY script command as a parameter. Whatever
name you give the variable (and the variables you choose to use as script
parameters) will only be meaningful to you ... and possibly to other
human-types. The script processor will store anything, anywhere, with no
regard to whether the actual variable name used 'makes sense'.
So how does FGETS 'remember' which line to get out of a file next?
The file I/O functions keep track of the "current position(s)" of all
open files (until FCLOSE is called) with what is known as the "file
pointer". FGETS and FPUTS simply move this pointer whenever you read or
write to the file.
If you use FPUTS, the data is written to the file (at the 'current'
pointer position) and the file pointer is then moved ahead by however
many bytes were written (plus two bytes for the Carriage Return/Line Feed
[CR/LF] FPUTS adds to the end of each line). The next time FPUTS is
called, it simply writes at the position of the file pointer and then
moves the pointer ahead again.
The same happens with FGETS, and the file pointer is moved ahead by
however many bytes were in the line you just read. The next time you
call FGETS it reads from the position of the pointer, then moves the
Intellicomm v2.01 SCRTUTOR.DOC 50
pointer again to get ready for the next FGETS.
FGETS is designed to read a single line of a text file, and it knows the
end of the line by the Line Feed written at the end of each line by
FPUTS. As soon as FGETS finds a LF, it stops reading the file, and moves
the pointer ahead by however many bytes it read, to get ready for the
next FGETS. The carriage return and line feed at the end of each line in
the file are stripped by FGETS before it stores the data in the variable,
since you'll rarely (if ever) want them.
If you open a file for "a"ppending (add to the end of the file), FOPEN
automatically moves the file pointer to the end of the file after it's
opened, so that the firs time you FPUTS to the file, the data goes
*after* any data already in the file (however, Ctrl-Z characters at the
end of the file, if any, are overwritten. FOPEN automatically checks for
Ctrl-Z's and moves the file pointer back so that the first FPUTS will
overwrite any existing Ctrl-Z's).
You can also read from and write to a file character by character by
using FPUTC and FGETC, which both move the file pointer ahead by one
character (one byte) per read/write. And you can FPUTS without adding a
CR/LF by using FPUTSNC (NC for 'N'o 'C'arriage return).
If you're wondering whether FGETS and FPUTS keep *separate* file
pointers, and keep track of where data will be both read from and written
to next, the answer is no. Reading from, and writing to the SAME file,
with a single FOPEN, is simply not something you'll need to do very
often. You 'can' do it, and we'll be discussing this below, but in all
the examples so far we have EITHER opened a file for "r"eading (which
means that writing is prohibited and if you attempt to FPUTS to the file,
the FPUTS is ignored) or "w"riting (which means that FGETS [reading] is
prohibited and FGETS is ignored). To both read and write, you must open
the file in a special way, and it's really not something you're likely to
have to do. What you'll usually do is FCLOSE the file, then FOPEN it
again in another 'mode' to switch from reading to writing. And FOPEN
automatically moves the file pointer back to the beginning (or end, if
opened in "a"ppend mode) of the file, so there's no need for separate
read/write file pointers.
NOTE 1: As mentioned earlier, unless you specify a path with the filename
in FOPEN, the file is created (or searched for, if opened for reading) in
the *current directory*. The DOS PATH does not apply when FOPEN is
looking for a file to open. Thus, you should always specify full
pathnames when using FOPEN. You can use the $HOME_DIR System Variable to
create files in the Icom home directory (where ICOM.EXE is), if your
script will be used by others. The various Icom system directories
ALWAYS have a trailing slash (even if you change a System Variable in
your script and forget to add the slash, one is automatically added), so
you can simply append filenames to these directories. Examples:
FOPEN f "c:\mydir\some.dat" "a" ;create or 'a'ppend to
FOPEN f "a:\catalog.txt" "r" ;open for 'r'eading
variable test_dat $HOME_DIR "test.dat"
Intellicomm v2.01 SCRTUTOR.DOC 51
;$HOME_DIR would contain C:\ICOM\ or the like. Note how we assigned
; multiple items to 'test_dat' with a single VARIABLE command. The
; end-result in variable 'test_dat' would be C:\ICOM\test.dat or the
; like.
FOPEN f test_dat "w" ;fopen "C:\ICOM\test.dat" "w"
;you can also open 'devices' in the same manner:
FOPEN f "PRN" "w" ;open device 'PRN' (printer) for writing
FOPEN f $PRN "w" ;ditto, but use the device the user has
; defined in the Icom main setup
FPUTS "This goes to the printer!"
FCLOSE f ;you still have to FCLOSE
However, do not attempt to open a COM port that Icom is using! [It's
okay if Icom isn't using it, and the port goes to a printer though.] The
file I/O functions are not very well suited to communications in any case
(it would work though, if you tried it on a port that Icom wasn't already
using), and you're much better off using the PORT command (open a new COM
port), then using SEND and the other COM-related commands, which were
designed for efficient port I/O.
NOTE 2: There is a limit in the number of files you can open with FOPEN
(before calling FCLOSE, that is. You can open as many files as you like
if you FCLOSE each file when you're done with it). The script processor
can accomodate up to ten open files at a time... The eleventh FOPEN with
no FCLOSE (i.e. trying to open eleven files at the same time... !?) earns
you an "FOPEN stack overflow" error which aborts your script. But since
it's really DOS that is handling the file I/O, you 'can' run into a
problem opening a file before ten files are open. You are also limited
to the number of DOS file handles the USER has set up in the CONFIG.SYS
"FILES=" statement. If you ever wondered what this statement was for...
now you know. As soon as DOS runs out of file handles, it prevents ANY
program, system-wide, from opening more files. FILES= determines the
total number of files that may be open on the system at the same time;
and the monitor, keyboard, and other devices use up some of these DOS
file handles before you even begin to run a program. [Your script does
not use up a file handle though, since Icom loads scripts into memory,
then FCLOSEs the script file.]
If you ran a program prior to your script (a multitasker, etc) which has
opened all the files DOS is set up to handle (FILES=)... you won't be
able to FOPEN any files in your scripts. FOPEN sets the $ERRORLEVEL
System Variable to 2 if no file handles are available, and if that
happens you can print a message to the user asking for an increase in the
FILES= of the CONFIG.SYS file:
fopen fh "TEST.DAT"
IF $ERRORLEVEL = 2
print "Unable to open file TEST.DAT due to a lack of file handles."
pause "Please increase the FILES= statement in your CONFIG.SYS
file...."
ENDIF
Intellicomm v2.01 SCRTUTOR.DOC 52
Various other problems can also occur when you FOPEN a file. The most
common is an invalid drive, path or filename (file not found) and in this
case, $ERRORLEVEL is set to 1 by FOPEN. If you attempt to open a "read
only" file in write mode, FOPEN sets $ERRORLEVEL to 3 (and prevents any
attempted writing to the file, since the file simply will not be opened
and you won't have a valid file handle). Thus, it's always wise to check
$ERRORLEVEL after an FOPEN to see whether the file was successfully
opened or not:
FOPEN "test.dat" "r"
if $ERRORLEVEL <> 0 ;$ERRORLEVEL = 0 if successful, <> is NOT equal to
msgwind "Can't find/open TEST.DAT"
if $ERRORLEVEL = 2 msgwind "Increase FILES= in your CONFIG.SYS"
exit
endif
The error above would most likely be due to the fact that TEST.DAT didn't
exist in the current DOS directory, but it could also mean that DOS is
simply out of file handles and that the user must increase the FILES=
statement in the CONFIG.SYS file.
NOTE 3: FOPEN allocates a memory buffer for file input/output, and only
writes to disk if/when the buffer fills up, or if FCLOSE or FFLUSH is
called. FFLUSH immediately writes all data from the memory buffer to
disk... unless a disk cache is in use. Disk cache write-ahead buffers
intercept calls to write to disk and do it in their own time... and
there's nothing you can do to prevent that, other than turning off the
write-ahead buffer of the disk cache by reloading it with the proper
command line switch.
'Buffering' data in this manner makes the disk read/writes more
effecient. On reads, FGETS (or even FGETC to get a single character)
will initially read in 512 bytes of the file to an internal buffer. When
you call FGETS/FGETC again it simply gets the data from the memory buffer
instead of going to the disk again, which is much faster (the disk can't
read less than 512 bytes [one sector] anyway). If a file was written to
(FPUTS, FPUTC, etc), by calling FCLOSE you cause any data in the write
buffer to be written to disk. I.e. FFLUSH is performed automatically
when you FCLOSE the file. In either read or write mode, you also free up
the memory that is used for the read/write buffer when you call FCLOSE.
Though the script processor will automatically FCLOSE any files you left
open when your script finishes, you should burn it into your brain now to
*always* call FCLOSE. When you write your scripts enter the FCLOSE while
you're thinking about it:
FOPEN f1 "\temp\myfile.txt" "w"
FCLOSE f1
Then go back and fill in the FGETS or FPUTS command(s) *between* the
FOPEN and FCLOSE, just to make sure you don't omit it (this technique
also works well with IF/ENDIF, WHILE/ENDWHILE, etc). There are
consequences in failing to FCLOSE an open file... If you attempt to
DELETE a file that is currently open, you can cause the computer to lock
Intellicomm v2.01 SCRTUTOR.DOC 53
up, or can cause an "exception 13" (which requires a re-boot) if the
script is running on a 386 processor or better. So always FCLOSE a file
as soon as you're done with it, to avoid such problems, and also to free
up the memory buffer FOPEN allocates for file buffering... and the file
handle DOS is using to keep track of the open file.
YET ANOTHER NOTE: File access is limited to the script from which the
file was FOPENed. You cannot FOPEN a file in one script, call another
script with the SCRIPT command, and then attempt to access the same open
file in the second script, using the same file handle (even if you use
the GlobalStr array to store the file handle). If you must access the
same file data in two or more scripts you must FCLOSE the file before
calling the second script and FOPEN it again in the called script.
5.3 Testing for End-of-File
If FGETS is successful (it was able to read data from the file) it sets
$ERRORLEVEL to 0. When you reach the end of the file, FGETS will set
$ERRORLEVEL to 1. To read an entire file, you'd do it from a WHILE loop,
testing $ERRORLEVEL in the loop:
WHILE 1 ;enter an endless loop (until a BREAK is found)
fgets s f1
if $ERRORLEVEL <> 0 BREAK ;if $errorlevel is not equal to 0
print s
ENDWHILE
5.4 How to store settings on-disk
If your script needs some sort of "main setup" data similar to
Intellicomm's main setup or BIF information, you can use the File I/O
commands to permanently store the settings. How you do this is really up
to you, and there is no "fixed" way of storing data on-disk. If you had
two "Yes/No" settings, you could store them on-disk like this:
variable setting1 "Yes"
variable setting2 "No"
variable mydatafile $HOME_DIR "MYSCRIPT.INI" ;C:\ICOM\MYSCRIPT.INI
variable f
;here you could prompt the user for the Yes/No settings and store
; the result in 'setting1' and 'setting2'.
;later, to permanently store the settings...
FOPEN f mydatafile "w" ;create or overwrite MYSCRIPT.INI
FPUTS f setting1 ;writes "Yes" (no quotes) to MYSCRIPT.INI
FPUTS f setting2 ;writes "No" (no quotes) to MYSCRIPT.INI
FCLOSE f
;each time your script started you could get the settings easily
FOPEN f mydatafile "r" ;open for reading
if $errorlevel = 0 ;opened okay?
Intellicomm v2.01 SCRTUTOR.DOC 54
FGETS setting1 f ;store Yes/No in setting1
FGETS setting2 f ;store Yes/No in setting2
FCLOSE f
endif
;and finally, later in your script when you want to test a setting
if setting1 = "Yes"
;do something if yes
else
;do something else if not
endif
You needn't store the words "Yes" and "No" though, and storing numbers
works just as well. Zero (0) is a standard way of saying "No" (or FALSE)
in computer programs, while non-zero is a standard way of saying "Yes"
(or TRUE). Thus you could substitute "Yes" and "No" above with 1 and 0,
and save disk space (maybe... disks allocate space in large chunks and
often it makes no difference whether a file has 1 byte or 1,000 bytes --
the same space must be allocated regardless) and speed up your script a
bit since numeric comparisons are faster than text comparisons:
if setting1 <> 0 ;not equal to zero means "Yes"
;do something if yes
else ;else (zero) means "No"
;do something else if zero (no)
endif
If you have multiple conditions, and not just Yes/No, just invent your
own system where 0 means one thing, 1 means another, 2 means another,
etc.
The format of the information you store in your data files is totally up
to you, and it's half the fun of designing your own programs.
5.5 Moving the File Pointer (Advanced Use Only)
To move the 'file pointer' of an open file (the thing that keeps track of
where FGETS will read from the file, and where FPUTS will write to next)
you can either FCLOSE and FOPEN the file again, which starts you back at
the beginning (or at the end if you open for "a"ppending), or use the
FSEEK command to move the pointer to a specific position without closing
the file. FTELL is FSEEK's companion, and it tells you where (at what
byte number in the file) the pointer currently is, if you need to know.
FSEEK seeks to an absolute byte in the file (byte 0 being the first
character in the file), INCLUDING all the carriage returns and line feeds
in the count. The only time it's of use is either when you know the
exact byte count to the data you need (you've designed the data file
yourself and have counted how many bytes there are to the position you
desire, and accounted for all the CR/LF's), or when you use the STRPAD
command with all your data before FPUTSing to the file, so that each line
(or 'record' ... it might be a group of lines) is the same length. If
all lines in the file are the same size, you can then just MULtiply by
the line/record length (plus the CR/LF), and FSEEK to the beginning of an
Intellicomm v2.01 SCRTUTOR.DOC 55
exact line/record in the file.
The best way to understand this is to look at the file in the same way
the file I/O routines do. They don't see a file line-by-line, they see
it as a single 'stream' of characters:
1 2 3
01234567890123456789012345678901 <-- Numeric Ruler for reference
only.
Line 1||Line 2||Line 3||Line 4|| <-- Data in the file
(The || above denote the CR/LF at the end of each line, the numeric ruler
denotes the positions of each character in the file.) So, looking at the
numeric ruler above, to FGETS line 1 you have to FSEEK to position 0. To
FGETS line 2, you have to FSEEK to position 8, line 3 is at position 16,
and line 4 is at position 24. All are multiples of 8, since each line is
8 characters long, counting the CR/LF at the end of each line.
To get any line out of the file above, you just DECrement the line you
want by one, then multiply by 8. If you want line 1, you multiply 0 by 8
(0 x 8 = 0), if you want line 4 you multiply 3 by 8 (3 x 8 = 24).
Decrement the line number, then multiply by the line length. Of course,
this only works if all lines are the same length... which is why
databases force you to define the length of each item before you store
any data in the file. By keeping each data item a specific length, it's
easy to locate a single piece of information out of the middle of a file.
For an illustration, let's assume you were storing a list of names and
you wanted later to be able to seek to the fifth name, or the tenth name,
etc., on request. If you pad each name with spaces before you write it
to disk, so that each line of the file is exactly 32 bytes long (30 for
the name, 2 for the CR/LF), you can later get any name easily:
variable name
variable f
FOPEN f "C:\MYDIR\NAMES.DAT" "a" ;open for "a"ppending (add to)
printnc "Enter a name: "
gets name 30
strpad name " " 30 ;pad the right side with spaces to ensure 30
; characters ... STRPADL pads on the left side
FPUTS f name ;write the data, followed by CR/LF
FCLOSE f
You could then retrieve any line from the file by decrementing the line
(record) you want, multiplying by the line length (record length), then
FSEEKing to that position prior to calling FGETS.
variable linenum
variable bytecount
variable name
variable size
printnc "Which name (record number) do you want? "
Intellicomm v2.01 SCRTUTOR.DOC 56
gets linenum 5 ;allow 5 characters for input (a number)
dec linenum ;linenum = linenum - 1
mul bytecount linenum 32 ;bytecount = linenum x 32 (32 = line len).
;now 'bytecount' holds the absolute byte count to the beginning of
; the line. If the user entered 1, we'd have decremented to 0,
; multiplied by 32, and ended up with 0 (32 x 0 = 0, the first byte of
; the file which would also be the start of the first line). If the
; user entered 2, we'd decrement to 1, multiply by 32, and end up with
; 32 (32 x 1 = 32, the position of the SECOND line), and so forth.
FILESIZE size "C:\MYDIR\NAMES.DAT"
;FILESIZE gets the size of a given file on disk and stores it
; in a variable
if size < bytecount ;file size less than bytecount?
print "No such record."
return
endif
FOPEN f "C:\MYDIR\NAMES.DAT" "r" ;open for "r"eading
FSEEK f bytecount 0 ;seek to the bytecount obtained above
FGETS name f ;get the line at bytecount
STRTRIM name ;trim off the trailing spaces
print "Here is the name you requested:"
print name
FCLOSE f
return
The 0 following 'bytecount' in the FSEEK above specifies where in the
file you want to seek FROM. 0 means to seek from the BEGINNING of the
file, which is what we want in this case. If 1 was specified after
'bytecount' instead of 0, FSEEK would seek ahead 'bytecount' bytes from
the CURRENT position of the file pointer (which would still work, since
the file had just been opened, and thus the 'current' position was the
beginning of the file). If 2 was specified FSEEK would seek ahead from
the END of the file. It's legal to seek past the end of the file, but
you end up with garbage in the file from the original end-of-file to the
position you seek to. An easy way to GET to the end of the file,
actually, is to use
FSEEK fhandle 0 2 (seek 0 bytes from the end of the file... it just moves
the pointer to the end and leaves it there).
You can also specify negative numbers in FSEEK to seek X number of bytes
BACKWARDS from the end of the file, or from the current position.
FSEEK f -500 2
The FSEEK above seeks 500 bytes back from the END of the file.
FSEEK f -32 1
The above would seeks 32 bytes back from the CURRENT position. You
Intellicomm v2.01 SCRTUTOR.DOC 57
cannot seek past the beginning though. If you attempt to, the file
pointer will stop at the beginning of the file.
Now, before we conclude the introduction and get to the more advanced
techniques, we might as well mention the only file I/O command we haven't
mentioned as yet: FPUTSNC. By now you should be quite clear on the fact
that FPUTS writes CR/LF characters to terminate the line after the data
you specify. In most cases this is what you'll want, but in some cases
you might want to write data to the file WITHOUT putting a CR/LF after
it. When this is the case, use FPUTSNC (NC meaning 'N'o 'C'arriage
Return). It simply writes the data you specify, and does not terminate
the line with CR/LF.
One more little tidbit: If you have to (or want to) write control
characters, such as your own CR/LF's to a file, just look up the control
character in the "ASCII Codes" online help line in Icom (available from
the help index and the "Script Language" index). Control characters are
specified by preceding the character with a caret; CR is ^M (Ctrl-M) LF
is ^J (Ctrl-J), etc. To write them (or any other control character) to a
file:
FPUTS f "Write this, then a blank line.^M^J"
FPUTS f "Now write this."
After executing the above, you'd end up with this in the file:
Write this, then a blank line.
Now write this.
The ^M^J terminate the first line, FPUTS adds its own ^M^J, and the
result is a blank line. You could accomplish the same thing with this:
FPUTS f "Write this, then a blank line."
FPUTS f "" ;write nothing, then ^M^J
FPUTS f "Now write this."
More details (and examples) on using FOPEN, FCLOSE, FGETC, FGETS, FPUTC,
FPUTS, FPUTSNC, FSEEK, and FFLUSH can be found in the Detailed Command
Summaries in SCRIPT.DOC when you need to refresh your memory. It's too
bad we don't have hard drives and file I/O routines built into our
brains....
5.6 Opening a File for Reading AND Writing (Advanced use only)
By adding a plus sign (+) to the file FOPEN mode you are allowed to both
read from (FGET) AND write to (FPUT) a file without closing it and re-
opening it. Examples:
FOPEN f "MYFILE.DAT" "r+" ;open MYFILE.DAT for read/write (file must
; exist)
FOPEN f "MYFILE.DAT" "w+" ;create MYFILE.DAT, allow reading and writing
FOPEN f "MYFILE.DAT "a+" ;create or append to MYFILE.DAT, allow reading
; and writing
Intellicomm v2.01 SCRTUTOR.DOC 58
Opening a file for both reading and writing is an advanced technique that
is not recommended until you have some experience with file I/O. Unless
you totally understand the 'file pointer' concept and FSEEK command,
you'll end up writing data all over the place in the file, possibly
overwriting useful data: Though you CANNOT overwrite data in other files
no matter where you FSEEK to, unless there's something wrong with your
disk. If you FSEEK past the end of the file, DOS simply thinks you're
trying to add to the file, and it looks for FREE disk space to write any
new data (and if it doesn't find any, it doesn't write any data and the
FPUTS will fail). DOS will *never* use the disk space that another file
is using, unless the other file was previously deleted, or unless your
disk or disk drive is malfunctioning (in which case you're going to lose
data anyway, whenever anything else writes to the disk).
In read/write mode, the file pointer keeps track of both where the file
will be read from AND written to next. Since you'll almost never want
data written at the point where the last read (FGETS/FGETC) left off, as
a safeguard you must call FSEEK before you switch from reading to writing
(from FGETS/FGETC to FPUTS/FPUTC) or vice versa. If you start writing to
the file, then want to switch to reading, you must also call FSEEK or the
read(s) will fail, and vice versa if you switch from writing to reading.
Normally, you will read several lines out of the file, then go to another
position in the file (FSEEK) and write data elsewhere (or vice versa...
keep this "vice versa" business in mind so I don't have to say it every
time. <grin>).
For example, you may wish to open a file for "r+" which opens it for read
write (as opposed to "w+" which destroys the file's contents if it
exists) and update a specific record in the file by FSEEKing to it, then
FPUTSing over the old data (remembering to pad the item with spaces so
that it overwrites ALL of the existing data in the file... more on this
in a second).
If you truly do wish to switch from read to write (the vice versa is
still in play), WITHOUT moving the file pointer just use 'FSEEK f 0 1'
(seek zero bytes from the current position... or leave the pointer where
it is). Here's an example of reading/writing with a single FOPEN:
variable s
variable f
FOPEN f "\TEMP\TEST.DAT" "w+" ;create TEST.DAT, allow reading/writing
FPUTS f "Line 1"
FPUTS f "Line 2"
FPUTS f "Line 3"
FSEEK f 0 0 ;seek 0 bytes from the beginning (1st byte in the file)
FGETS s f
print s ;prints Line 1
FGETS s f
print s ;prints Line 2
Intellicomm v2.01 SCRTUTOR.DOC 59
FGETS s f
print s ;prints Line 3
FSEEK f 8 0 ;seek to 8 bytes from the beginning (start of Line 2)
FPUTS f "Item 2" ;overwrites 'Line 2' with 'Item 2'.
FCLOSE f
If you FPUTS a line that is LONGER than the previous line in the file
(e.g. FPUTS f "This is line 2") then you'd overwrite the CR/LF at the end
of the previous 'Line 2' and can overwrite the beginning of 'Line 3' as
well. If you write a line that is SHORTER than the existing line, then
some of the old data remains intact. If we used:
FSEEK f 8 0 ;seek to start of line 2
FPUTS f "2" ;write a 2 at the beginning of line 2
FCLOSE f
If we did this instead where we overwrote "Line 2" with "Item 2" above,
we'd end up with "2||e 2" in the data file... (where || denote the CR/LF
written by FPUTS) since it didn't overwrite the entire line. The "e 2"
after the CR/LF is the remainder of the old "Line 2".
It works no differently than using Typeover mode in your text editor or
word processor. Think of the file as a SINGLE line of characters, and
imagine what would happen to that line if you loaded it into your Editor,
moved the cursor to a specific position (FSEEK), turned on Typeover mode
and started typing. That's what FPUTS is doing when you FSEEK to a
specific position and start writing. There is no way to 'insert' data
into an existing file on-disk (though you can pretend you did... more on
that in a second), anymore than you can 'insert' data on your video tapes
or cassette tapes without overwriting what was there previously. Again,
if you STRPAD your data before writing it, you'll always have a few
blanks at the end of the line to store longer items later. And if you
STRPAD shorter lines, you won't end up with the remainder of the existing
(longer) data as was illustrated above.
5.7 Upating an Old Data File
The only way to change the existing structure (line length) of a text
file is to FOPEN the existing file for reading, read each line, pad it to
a longer length, then write the data to another file (similar must be
done to add new lines between existing ones):
variable datfile $HOME_DIR "TEST.DAT" ;C:\ICOM\TEST.DAT
variable tmpfile $HOME_DIR "TEST.$$$" ;C:\ICOM\TEST.$$$
variable dat ;for file handles
variable tmp
variable s ;for file data
FOPEN dat datfile "r" ;open TEST.DAT for reading
FOPEN tmp tmpfile "w" ;create/overwrite TEST.$$$
if $errorlevel <> 0 exit ;exit if error opening either file
while 1 ;endless loop (until BREAK)
Intellicomm v2.01 SCRTUTOR.DOC 60
FGETS s dat ;read in a line from TEST.DAT
if $errorlevel <> 0 break ;end of file? (no data in 's' if so)
strpad s " " 50 ;pad it to the new length
FPUTS tmp s ;write the longer line in TEST.$$$
if $errorlevel <> 0 ;uh oh... we ran out of disk space
fclose tmp ; close 'em up, and avoid that
fclose dat ; DELETE datfile below like the plague
delete tmpfile ;we can't use this anymore, lose it
print "Disk full. Aborting."
exit
endif
endwhile
fclose tmp
fclose dat
delete datfile ;delete TEST.DAT
rename tmpfile datfile ;rename TEST.$$$ to TEST.DAT
So, if you ever wondered what those .$$$ files were that you found lying
around on your disk, now you know. A program either forgot to clean up
its temporary file (I hope it wasn't Intellicomm), or perhaps the power
went out or the machine hung before it got a chance to delete it. The
extension .$$$ is often used to avoid overwriting useful files. You
'could' use the filename "ICOM.EXE" for your temporary file, but TEST.$$$
(or anything .$$$) is probably safer.
Experimentation is the best way to get a feel for file I/O. Create a
dummy text file to play around with, and run a few FGETS/FPUTS tests on
the file to see what happens (load the text file into the editor and look
at it after each test). "Seeing is believing" and you really have to
experience file I/O first-hand to get a good feel for it.
REMEMBER:
o Always either perform a CHDIR to get to the proper drive/directory, or
specify the full D:\PATH\FILENAME.EXT in FOPEN when you use it.
Otherwise the CURRENT DIRECTORY is assumed, which could be painful if
you FOPEN for "w"riting, and are using a common filename... and an
important file of the same name exists in the current directory.
o Never attempt to DELETE or RENAME an open file.
o Make sure the file pointer is at the proper position before you FPUTS
(or FPUTSNC, FPUTC) to overwrite old data in an existing data file.
You can only do this if you know the exact format of the data file
(i.e. if you created it yourself or otherwise have a reliable way of
knowing) and use FSEEK prior to the disk write to move the file pointer
to the proper position. Once you FPUTS you cannot UNFPUTS, and an
FFLUSH will only ensure that the data is written to disk faster.
Further, if you overwrite existing data, the NEW data must be of the
same length as the old data (or must be padded with spaces), or you
will either overwrite the beginning of the next line, or will end up
with a portion of the old data after the new data.
Intellicomm v2.01 SCRTUTOR.DOC 61
o FPUTS writes a Carriage Return/Line Feed after data, and FGETS uses
this CR/LF to determine when to stop reading data from the file (it
reads one line, and stops when it hits the LF). You must take the
CR/LF into account when calculating byte counts with FSEEK.
o Always FCLOSE a file as soon as you're finished reading/writing to it.
o Pat yourself on the back. These same concepts (though in slightly
different syntaxes) are used for file I/O in all computer programs, and
all programming languages. If you got through all of the above, and if
you actually practice it and succeed, you could consider yourself a
fairly able computer programmer.
Intellicomm v2.01 SCRTUTOR.DOC 62
6. INTRODUCTION TO DATABASE COMMANDS (FILE TAGGER CATALOGS)
This section introduces you basic database concepts and will help you to
make effective use of the script database or "File Tagger Catalog
oriented" commands. The material is somewhat advanced (though not
terribly so), and you should have a basic grasp of scripts such as
variables, the ASSIGN command, WHILE loops, etc., before you tackle this
section.
6.1 Why would I want to use the catalog-oriented commands?
The main purpose of the catalog-oriented commands is to allow you to
perform customized maintenance on your Tagger catalogs. Using job Custom
Commands, or the PREJOB.SCR, PREDOWN.SCR or POSTFILE.SCR scripts (see
"Automation in Detail" in the Icom online help for details on these
scripts) and executing a script prior to a Call to a BBS, you could scan
a given Tagger catalog and automatically Tag (or Untag) certain files, or
set Transfer Days (the day of the week on which to transfer the file)
based on file criteria such as the filesize. Or, you could add up the
total number of bytes of all Tagged files and change the tag status of
certain files to a special tag of your own which would allow you to
temporarily untag the file and re-tag it later after the job completed,
and so forth. Or you could modify the description (or any other
attributes) of files after downloads by adding to POSTFILE.SCR. In
short, by getting a handle on the File Tagger-oriented commands, you'll
gain absolute and total control over your File Tagger catalogs, in every
imaginable way, in most every aspect of use.
Many requests have come in for specialized File Tagger catalog handling
that really wouldn't benefit the majority of Intellicomm users, but
certainly would be helpful to those people who made the requests. Using
the catalog-oriented commands, you can handle almost any specialized
catalog-oriented task you can dream up -- and you can handle it exactly
as YOU need it done, without being forced to use "pre-fabricated"
ICOM.EXE routines which might not do exactly what you want.
6.2 What is a Database?
Databases (and File Tagger Catalogs which are dBASE-compatible
databases), in the context we are interested in here, are files that hold
information on-disk in a specific format. They are made up of simple
units called "fields" and "records":
"Field 1, Record 1" |
"Field 2, Record 1" |---- RECORD 1
"Field 3, Record 1" |
"Field 1, Record 2" |
"Field 2, Record 2" |---- RECORD 2
"Field 3, Record 2" |
...etc. Each record contains a given number of fields, and each field
holds a specific type of information, of a specific length. In File
Intellicomm v2.01 SCRTUTOR.DOC 63
Tagger catalogs, the "fields" are the filename, file date, import date,
description, etc., and each "record" holds information about ONE file
that exists on a BBS (if accessing the NEWFILES catalog, which holds BBS
new files listings) or on your disk (if accessing the FILELIST catalog,
which keeps track of the files Icom auto-downloads to let you re-upload
them to other BBS's).
The importance of keeping each field a specific length, as opposed to
storing data all different lengths, is that it makes it very easy (and
fast) to access any given record in the database. Since each field is a
fixed size (regardless of the actual data being stored in the field),
each record is the same size and thus the position of any given record in
the file is easy to obtain. If the combined length of all fields in all
records is 50 bytes, and we want record #10, we simply multiply 50 by 10
to find the position of the record, in the database file.
If the fields/records were NOT all the same size, it would be difficult
(and slow) to pull a given record out of the database, since we'd have to
start at the beginning, and 'count' records (perhaps using a special
character between each record so we knew where each record ended) until
we got to the one we wanted.
dBASE-compatible databases (and File Tagger Catalogs) use the file
extension .DBF (e.g. NEWFILES.DBF, FILELIST.DBF, etc), and each record in
the file has a number starting at record #1, up to a maximum of (about)
record #2 billion. To get at any of the records, we (or something else,
such as the indexes discussed below) must first know the record number.
In File Tagger catalogs, record #1 is reserved for a "header" record,
which is where the Tagger saves information such as the bookmark
position, the current View Date of the catalog, the sort order and
direction as set by the user, etc. The header record is no different
than any of the other records (it has the exact same fields... it has
to), other than the fact that the File Tagger reserves it for its own
use, to store information. You cannot access record #1 directly, though
you can update most of the information in the header (such as the catalog
sort order, bookmark position, current view date, etc) with special
script commands which are outlined later in this section.
The rest of the records in the File Tagger catalogs hold information
about files, such as the filename, date, size, whether the file is Tagged
for transfer or not, etc.
6.3 Indexes
Database index (.NDX) files are special files which allow easy sorting of
one particular .DBF database in a specific way. When new records are
added to a database (.DBF), they are not 'inserted' into the existing
record structure, but rather are simply added (appended) to the END of
the .DBF file. If you have 10 records in your database, and add one, the
new record becomes record #12 (12 because record #1 is used as a header,
as mentioned above. The header (record #1) plus 10 existing records
(records 2-11), plus the new record #12).
Intellicomm v2.01 SCRTUTOR.DOC 64
However, as mentioned above, to get a specific data record from the
database, we must have the record number. Now suppose you want to get
all the Tagged records out of a Tagger Catalog that has 5,000 records in
it. We already know that the .DBF itself doesn't sort the records in any
specific way, and simply appends new records to the end ... so do we have
to read record #2, check for a Tag, then read record #3, check for a Tag,
all the way up to 5,000 records? Thankfully, no, since it would be very
time-consuming (and disk-punishing) to do so. This where the .NDX files
come into play.
When an .NDX is in use the .DBF file is NOT accessed in its natural
record order. When you access the 'first' record in the database, you
might get record #5,000 while the 'last' record might be record #25.
I.e. the indexes give us the proper .DBF record numbers needed to display
(or otherwise manipulate) the database according to a specific sort
order. When an NDX is in use, the database routines jump all over the
database, perhaps grabbing record #5, then record #50, then record #12,
etc., though this is mostly invisible to you.
How the NDX's actually accomplish this sorting is not important: The
important thing to understand is that when an index is in use, the
'first' record in the database is not necessarily record #1 (or #2 in our
case, since record #1 is reserved for the header). The 'first' record is
simply the record that happens to sort first (alphabetically,
chronologically, or otherwise, according to how the index is defined),
given a specific sort order, as compared to all the other records in the
database. The indexes do all the sorting, and it takes zero work on your
part. It is important to understand the CONCEPTS behind indexes, since
sometimes you WILL be involved with the actual database record numbers,
and it could be confusing to see record #5,000 coming 'first', then
record #10 coming 'next', etc., were you not aware of how the .NDX(s) and
.DBF work together.
6.4 Using File Tagger Indexes
The File Tagger maintains three index (.NDX) files per File Tagger
catalog, and stores them all in the same directory as the .DBF itself
(the $DBF_DIR System Variable can be used to access the proper
directory). The indexes use the same base name as the catalog
("NEWFILES" is the "base" name of the NEWFILES.DBF database), but replace
or add a new letter at the end of the base name. Below we'll use the
base name NEWFILES to illustrate the filename of each index (you don't
"have" to know the .NDX filename except when you want to delete a
catalog, or rebuild a damaged index):
1. "Tag Status/Location" (NEWFILET.NDX ... replaces the "S" at the end
of NEWFILES with a "T" since the filename is already 8 letters; "T"
for "Tag Status"), used to easily get all the Tagged (or Noted, or
Untagged) files for a specific BBS.
2. "Catalog Date/Filesize (NEWFILEC.NDX ... "C" for Catalog Date), the
oldest records to the newest records, the smallest to the largest.
This index makes for quick and easy purging (deleting) of older
records. The "Catalog Date" is the date on which the record was
Intellicomm v2.01 SCRTUTOR.DOC 65
added (imported to) to the database.
3. "Filename/File Date" (NEWFILEF.NDX ... "F" for Filename) all
filenames in the database, in alphabetical order. This index makes
for quick duplicate checking (so we needn't search filenames of all
the records in the catalog when importing) and other filename
searches.
The script COPEN command ("C"atalog OPEN; all catalog-oriented commands
begin with "C" for Catalog) opens the .DBF (database), all three
associated indexes, and the .DBT (standard dBASE "memo" file which holds
the extended descriptions of files). If any or all of the .NDX files are
missing, COPEN automatically builds new ones. And thus a handy way to
build new indexes, if the need ever arises, is to simply use the DELETE
command to delete the one or more .NDX files (after the catalog is
CLOSED; do not delete open NDX files), then call COPEN and it will build
new ones based on contents of the .DBF file.
IMPORTANT: When an existing record is deleted from the database
(permanently in a CPACK, not when it's just flagged as "Deleted"), or a
new record is added, all three .NDX files are *automatically* updated or
rebuilt entirely to insert the new record data in the proper place
according to each index sort order. You will never work with an .NDX
file directly ... it's all taken care of automatically by the File Tagger
(and the Tagger-oriented script commands), as new records are added, and
old records are deleted. I.e. you never have to "maintain" an .NDX from
your script. You simply "use" them (by setting a sort order with the
CSETSORT command), and let Icom take care of the boring maintenance
details.
6.5 How this all applies to Scripts
Accessing a File Tagger catalog is much the same as accessing a regular
text file with the File I/O script commands:
1. Open the catalog with COPEN.
2. Set the sort order (.NDX) if necessary with CSETSORT. Note that all
three of a catalog's NDX files are 'opened' when you perform a COPEN,
and CSETSORT simply switches from one index to another.
3. Use CGETREC to read the first record. The next call to CGETREC gets
the next record, etc, according to the sort order (index) you set with
CSETSORT. Again, the 'first' record changes from sort order to sort
order, and most likely will NOT be record #2 (the first valid record
in the database, due to the header) in the .DBF file. After you CGET
a RECord ('get' meaning load it from disk into memory) you can modify
it and write it back to the database (CPUTREC) or can display it, Tag
it, Note it, Untag it, etc. *Until* you CGETREC a record from the
database you can't do a thing with it: the record must be loaded from
disk into memory before you can access or manipulate it.
4. Close the catalog (when finished) with CCLOSE.
When you use CGETREC to load a record from the database (again, 'load'
meaning that it is read from the database on-disk and loaded into memory
where you can work with it), the record is split up into the "fields"
Intellicomm v2.01 SCRTUTOR.DOC 66
mentioned at the outset. Each field goes into a different script System
Variable as listed below. These fields listed below are exactly the
fields you see when you "Edit" a record from within the File Tagger:
$CTAG_FLD The tag status of the file (Tagged, Noted, Untagged). With
inventive use of this field (if need be), you can set your
own custom tags which will be meaningful to your script, but
will be meaningless to Intellicomm itself when transferring
files. $CTAG_FLD field normally holds a 'T' if the file is
Tagged, an 'N' if the file is Noted, and a 'U' if the file
is untagged. Icom ignores any letters from 'U' to the end
of the alphabet, leaving you with several possibilities for
"custom" tags which you might use to, say, automatically
Untag a very large file, marking the $CTAG_FLD with the
letter 'Z'. When Icom finishes transferring files (it will
ignore your custom 'Z' tags), you can then go through the
catalog looking for your 'Z' tags, and can then re-tag the
files with the regular 'T' for Tag. You'll be able to
understand how you might accomplish this better after you
finish this chapter.
$CDAY_FLD The transfer day, if set by the user or by a script. This
field can be compared against the $DOW (Day of Week) System
Variable to see if the file should be transferred 'today' or
not. If $CDAY_FLD = "0" (Anyday, the default), the user
cares not on which day the file is transferred. This one
can also be used to force Intellicomm to ignore a Tagged
file; Icom checks this field when transferring files, and
won't bother with it if $CDAY_FLD doesn't match $DOW (the
current day of the week).
$CNAME_FLD The FILENAME.EXT of the file. No D:\PATH\ is ever stored in
this field, even in the FILELIST (upload) catalog. Icom
keeps a list of Upload Directories (defined in the Icom main
setup on the "Filenames and Paths" screen; accessable from
scripts via the $UL_PATH item) and looks in those
directories to find files when uploading. You do the same
with the LOCFILE (Locate File) script command.
$CFDATE_FLD The file (or archive) modification/creation date, in
standard dBASE date format: YYYYMMDD [Year, Month, Day]
$CSIZE_FLD The size of the file.
$CCDATE_FLD The date that the record was added to the catalog, in
standard dBASE date format (the CATALOG DATE: this is what
the "View Date" uses to filter out older files, and the View
Date is still in effect when you access a catalog from a
script ... though you can change the View Date from your
script if need be).
$CLOC_FLD The BIF ID/filename (the BBS that has the file). If you are
online when your script is accessing the Tagger Catalog you
can compare this field against the $BIF_NAME System Variable
to see if the file exists on the BBS Icom is currently
connected to.
$CAREA_FLD The BBS conference/forum the file is in (NEWAREA $CAREA_FLD
can be used if online to change to the proper BBS area;
assuming the BIF is set up for it with the proper "Area
Change" command on the BIF "File" screen).
Intellicomm v2.01 SCRTUTOR.DOC 67
REMEMBER: CGETREC 'loads' a single catalog (.DBF) record from disk into
memory (with each field from the record going into the variables listed
above). Thus you cannot (if you want anything valid) access the above
variables above *until* you COPEN a catalog, and use CGETREC to load a
record from disk.
There are two exceptions to this: During automated file transfers, and
in the POSTFILE.SCR script (see the comments in POSTFILE.SCR included
with Intellicomm). During automated file transfers Intellicomm will have
already performed a COPEN (and perhaps a CGETREC) on the files it's
automatically transferring. There are four places in the BIF where you
can have a script called during automated file transfers (all are on the
BIF "File" screen): the "Area Change" "Upload File[s]", "Download
File[s]", and "Description [@SCRIPT]" slots. By entering @SCRIPTNAME in
any of these BIF slots, Icom will run a script instead of sending a
single command (as is the case with all BIF responses). The only place
you really 'should' place a @SCRIPTNAME command during an automated file
transfer, if necessary, is in the "Description [@SCRIPT]" command. The
purpose of this is to handle entry of the file description (on uploads)
in tricky situations where the usual description entry routine can't
handle. If you call a script via this BIF slot, Intellicomm will already
have performed a CGETREC, and the fields listed above (plus the
description, outlined below) WILL contain valid values, without your
opening the catalog with COPEN. If you do use a script during automated
file transfers, DO NOT close the catalog (CCLOSE), or set the sort order
to the Tag Status/Location index and move the pointer (i.e. perform a
CGETREC) or Icom will lose its place.
If you change any of the variables above after a CGETREC, you do NOT
modify the actual record in the database -- you modify only memory
variables. To modify a record permanently you must re-write the data
back from memory to the disk using CPUTREC. This is typical of file I/O
on computers, and you almost never work with a disk file directly, in any
computer program (unless writing your own version of DOS). We "work"
with the computer's memory (very fast), and use the disk (DOS file
Input/Output; extremely slow in computer terms) only when permanent
storage is required.
You may notice above that the file DESCRIPTION isn't stored in a System
Variable, and this because no script variable can hold more than 256
characters of text in Intellicomm's script language; yet descriptions
will often contain over 256 characters. Thus, CGETREC loads the entire
description into a large internal memory buffer and two script commands
-- CGETDESC and CPUTDESC -- are used to get a single line of the
description from this buffer, and load it into a variable where you can
work with it one line at a time.
Again, CPUTDESC does not modify the description on-disk in the catalog.
It simply affects the description (line by line) in an internal memory
buffer. So you can 'CGETDESC myvariable 1' to get description line 1,
modify it, then 'CPUTDESC myvariable 1' to write the modified line 1 back
to the memory buffer (the old line is stripped first, so if you CPUDESC a
blank line, you remove the original description line). To update a
record (description included, as well as updating of the .NDX files) on-
Intellicomm v2.01 SCRTUTOR.DOC 68
disk you must call CPUTREC.
One more note on descriptions: When loaded into the memory buffer by
CGETREC, descriptions are formatted to 45 characters per line (wrapping
words that don't fit on the 45 character line, in the same way as a word
processor does). After using CGETREC you can re-format the description
to another line length by calling CFORMATDESC. For example you wanted 40
character description lines you would:
CGETREC ;get the record (more on this in a second)
CFORMATDESC 40 ;format to no more than 40 characters per line
CGETDESC somevariable 1 ;put description line 1 (now 40 chars or less)
; into variable 'somevariable'
Now before you get completely lost, it's time for an example you can sink
your teeth into. Let's display the NEWFILES catalog, sorted by Tag
Status/Location. Displaying a catalog isn't something you're likely to
do with a script (the File Tagger can most likely do a better/faster job)
but the concepts of cycling through the catalog records are the same
whether you're displaying them or downloading files, or checking and
modifying records, or doing anything else. A good way to get practice is
to go through a catalog DISPLAYING various things on the screen yourself,
so you can get a feel for it. Make sure you read the comments (;) below:
variable key ;first we need a few variables for use below
variable tag
variable name
variable date
variable desc
wndopen "NEWFILES Catalog" 1 1 80 25 ;open a box to display in
print " ^BFilename Date Description^B"
window 2 3 79 24 ;so we don't overwrite the titles
COPEN "NEWFILES" ;open the .DBF and its .NDX's
;Next, set the sort order. CSETSORT takes two parameters: the first
; is the sort order (-1 = unsorted, 1 = Tag Status/Location, 2 = Catalog
; Date/Filesize, 3 = Filename/File Date) and the second is the sort
; DIRECTION (0 [or nothing] = forward, 1 = reversed)
CSETSORT 1 ;Tag Status/Location, FORWARD: Noted records come first,
; Tagged records come second, Untagged records come last.
; If we reverse the sort order, Untagged records come
; first, Tagged second, and Noted third.
;Note that if no parameters are specified after CSETSORT (or if you
; specify anything other than -1, 1, 2, or 3 as the sort order), the
; user is prompted to enter the sort order and direction (identical to
; when "Sort" is selected from within the File Tagger), which is a handy
; way to get user input when needed. Note also that CSETSORT does NOT
; change the sort order as saved in the catalog itself (i.e. the order
; the user has set). You must use CSAVESORT to save the sort order to
; the catalog header, if you want to keep the sort order 'permanently'.
Intellicomm v2.01 SCRTUTOR.DOC 69
;If you do not call CSETSORT after COPENing a catalog, it will remain
; sorted in the same manner as the user had it set up the last time it
; was viewed, which probably isn't what you'd want. You can check the
; current sort order, if need be, by accessing the $CSORT_ORDER and
; $CSORT_DIR (direction) System Variables. They contain the same values
; outlined in the CSETSORT parameters listed above.
while 1 ;enter an endless loop (until BREAK)
CGETREC ;gets first (or next) record
if $errorlevel <> 0 break ;end loop at end of catalog
if $CTAG_FLD = "T" assign tag "^P" ;T = Tagged (^P = > as in Tagger)
if $CTAG_FLD = "N" assign tag "»" ;N = Noted
if $CTAG_FLD = "U" assign tag " " ;U = Untagged
assign name $CNAME_FLD ;copy the filename into our variable
strpad name " " 14 ;pad filename with spaces to 14 chars
;as mentioned earlier, $CCDATE_FLD and $CFDATE_FLD are stored in
; standard dBASE date format YYYYMMDD. We don't want to display the
; date like this, so we call CDATE2DATE to convert it to the usual
; date format/date separators the user has set up in the Icom main
; setup.
CDATE2DATE date $CFDATE_FLD ;the result is stored in 'date'
CGETDESC desc 1 ;get description line 1
strblank desc assign desc "No description available"
print "^B" tag "^B" name date " " desc ;print the data (^B is Bold)
endwhile
CCLOSE ;and close the catalog when done
pause "^M^J^BPress a key..."
wndclose
return
What happens above is that the commands between WHILE and ENDWHILE are
executed over and over again, until the end of the catalog is found
(CGETREC sets $ERRORLEVEL to 1 when it hits the end). So CGETREC is
called repeatedly and each record is displayed, until the end of the
catalog is found.
When COPEN or CSETSORT are called, the 'record pointer' that CGETREC uses
to know which record to get from the database, is automatically set to
the 'beginning' of the catalog (the 'beginning' according to the sort
order). After CGETREC gets the record and stores the data in the
$CXXXX_FLD System Variables, it updates an internal record pointer (using
the current .NDX) to point to the 'next' record, again, according to the
current sort order. So each call to CGETREC gets the next record,
according to the current sort order.... it couldn't be simpler.
You can use the CTELL command to find out which database record number
CGETREC got, and if you used CTELL in the loop above and printed out the
record numbers, you'd probably see that CGETREC was jumping all over the
database getting perhaps record #5, then record #30, then record #10,
etc.
You don't 'have' to know the current record number except in one case:
when CDELREC is used to delete a record. For safety's sake, you must get
Intellicomm v2.01 SCRTUTOR.DOC 70
(with CTELL) and specify a record number to delete when you use CDELREC.
With most other catalog-oriented script commands that take a record
number (several do, though optionally), if you OMIT the record number,
the 'current' record (the last record obtained with CGETREC) is assumed.
6.6 The View Date
As mentioned earlier, the View Date is still in effect when accessing a
catalog from a script... which means that records with a $CCDATE_FLD
(Catalog Date) *older* than the View Date ($VIEW_DATE) are filtered out.
I.e. CGETREC ignores these older records. The one exception to this is
if you use CSETSORT -1 (no sort order). In this case, the View Date is
ignored, and database records are accessed in their raw order (as they
were added to the .DBF), and CGETREC gets all records regardless of their
Catalog Date.
You can check and/or modify the View Date from your scripts by accessing
the $VIEW_DATE System Variable. If you modify $VIEW_DATE, the date must
be stored in standard dBASE format: YYYYMMDD (YearMonthDay). Examples:
ASSIGN $VIEW_DATE "19900101" ;January 1st, 1990. Setting this View Date
; will ensure that ALL existing records
; are found by CGETREC (and CSEEK,
; discussed below). Intellicomm was not
; released by that date, so it's impossible
; for a record to have a catalog date older
; than 01/01/90.
variable dbase_date
DATE2CDATE dbase_date $DATE ;convert today's date to dBASE format
ASSIGN $VIEW_DATE dbase_date ;only records imported today are found by
; CGETREC (and CSEEK), if an index is in
; use
Note that neither ASSIGN $VIEW_DATE example above would change the View
Date as saved in the catalog itself. If you wish to save View Date
changes to the catalog header (i.e. so it will take effect the next time
the user views the catalog, or the next time you open it with COPEN), use
CSAVEVDATE.
Note also that the File Tagger itself automatically updates the View Date
to the current date ('today') when it imports BBS new files listings, so
that the user only sees the new files that were imported. This is the
date that $VIEW_DATE will be set to when you first COPEN a catalog: the
date of the last import.
The 'Auto View Date Update' on imports is a feature that can be turned
off in the Icom main setup though (Tagger Settings), and you can also
check that setting or temporarily shut it off in your script by accessing
the "*afdate" Main Setup Variable (apologies for the 'f' ... The View
Date was originally named the Filter Date, and 'af' stands for 'auto
filter' date. Converting the tag in the ICOM.INI file from 'afdate' to
'avdate' wasn't worth the trouble). SAVEINI re-saves the Main Setup data
to disk if you wish to permanently change this or any other Main Setup
Variable from a script. See the Main Variable summary in SCRIPT.DOC for
Intellicomm v2.01 SCRTUTOR.DOC 71
details on this or any other Main Setup Variable.
6.7 Getting Around in a Catalog
You may have noticed in the main example above that while there are a
couple of twists as far as fields and sorting goes, access to the
database was sequential (one record after the other), much like reading a
text file with FGETS. And just as you can move the file 'pointer' around
with FSEEK when reading text files so that FGETS reads at a different
position in the file, CSEEK allows you to move the 'record pointer' of
the database so that CGETREC gets one or more records out of sequence.
It works in much the same way as FSEEK (don't worry if you've never used
FSEEK; it isn't mandatory for this discussion), except that you never
need worry about the specific file positions, since the database
structure does all the work for you.
CSEEK takes two parameters: the first is "the number of records FROM
whence" and the second is whence (0 = from the beginning of the catalog,
1 = from the present position, 2 = from the end of the catalog, 3 = from
anywhere; it seeks to an absolute record number). Examples:
CSEEK 0 0 ;seek 0 records from the beginning (take it from the top)
CSEEK 10 0 ;seek to the tenth record from the beginning
CSEEK 1 1 ;seek ahead (from current position) by one record
CSEEK 10 1 ;seek ahead (from current position) by ten records
CSEEK -10 2 ;seek ten records back from the end
CSEEK 100 3 ;locate record #100 in the index (100 must be a valid rec#)
As mentioned earlier, CSETSORT automatically puts you back at the top of
the catalog (an automatic CSEEK 0 0 is performed after CSETSORT). If you
want to change the sort order with CSETSORT, but DON'T want to lose your
current position in the catalog, first GET your current record number
with CTELL, then either use mode 3 of CSEEK (seek to an absolute record
number) or specify that record number with CGETREC to re-load it, and to
seek back to that record in the index:
variable cur_rec ;use any variable to store the record number
CTELL cur_rec ;stores current record number in 'cur_rec'
CSETSORT 2 1 ;set a new sort order
CSEEK cur_rec 3 ;seek back to the previous record number
CGETREC ;gets record 'cur_rec' as stored by CTELL
;alternatively, you can do this
CTELL cur_rec ;stores current record number in 'cur_rec'
CSETSORT 2 1 ;set a new sort order
CGETREC cur_rec ;"CSEEK cur_rec 3" implied before CGETREC
It may seem like there's a valid need for this (and perhaps you'll find
one... though the only place this sort of thing is used in Icom is when
DISPLAYING the catalog to the user, and s/he changes the sort order... so
we keep the pointer on the same record). But in most cases, once you
change the sort order you might as well start back at the beginning,
Intellicomm v2.01 SCRTUTOR.DOC 72
since the 'current' record (as reported by CTELL before the change in
sort order), although it still has the same record number, will now be in
a completely different position in the database, RELATIVE to where it was
before the sort order was changed. If it was the 20th record previously
(in relation to the 'top' of the catalog according to the last sort
order), it might be the 50th record from the 'top' after a change in the
sort order, or may the the last record in the database, or the first,
etc. Further, the "next" and "previous" records before and after
'cur_rec' will not be the same as they would have been, had the sort
order not been changed. Even the first and last records in the database
will be different. [Speaking in relation to other records, according to
the sort order. The database record numbers always remain the same, but
the record numbers are mostly irrelevant when an index is in use.] Thus
there's little use in going back to the same position after a re-sort,
since it's not the 'same' position at all, and none of the other records
that were around the current record previously will remain the same
either.
If you're confused, you can see this quite clearly by entering the File
Tagger and selecting "Sort" (in a catalog that contains a screenful of
records or more) to change the sort order. The 'pointer' remains in the
same position, but all the records around the pointer change... and the
position of the current record in relation to the 'top' of the catalog
(as evidenced by the little box in the scrollbar, on the right border of
the Tagger) changes as well. It's a whole new ball game after a change
in the sort order, and thus normally you'll only perform *one* CSETSORT,
just after the COPEN. In such a case, you'll also probably want to start
at the top of the catalog, which is what CSETSORT automatically does.
6.8 Getting the Total Number of Records
Before using CSEEK, you can get the total number of records in the
database if necessary by checking the $CTOTAL System Variable. And, if
an index is in use (if CSETSORT -1 is used, no index is in use) $CVTOTAL
tells you how many records are visible according to the current View Date
($VIEW_DATE). In most cases, if using a sort order, $CTOTAL will not be
the same as $CVTOTAL, since the View Date is normally set to filter out
older records. Example:
variable num_filtered
;Below is a 'SUBtract': num_filtered = $CTOTAL - $CVTOTAL
; This gives us the number of records that are currently being filtered
; out (or zero if none).
SUB num_filtered $CTOTAL $CVTOTAL
print num_filtered " records are hidden due to the View Date."
The above displays the number of records that are currently being
filtered out due to the View Date. Note that $CVTOTAL is calculated when
you open (COPEN) a catalog, and is re-calculated whenever you change the
$VIEW_DATE variable.
Intellicomm v2.01 SCRTUTOR.DOC 73
6.9 The View Date and Tagged/Noted Files
Tagged files are NEVER filtered out by the View Date, whether an index is
in use or not. And Noted files are not filtered out by default, but that
can be changed by the user in the Icom Main Setup on the File Tagger
Settings screen. If "View Date Filters Noted?" is set to Yes in the Icom
Main Setup, then the Noted files imported prior to the current View Date
WILL be ignored (filtered out) by CGETREC and CSEEK, if an index is in
use. You can change this if need be by accessing the *fnote (filter
noted) Main Setup Variable:
ASSIGN *fnote 0 ;View Date doesn't filter out any Noted files
ASSIGN *fnote 1 ;View Date does filter out (older) Noted files
If you modify this or any other Main Setup Variable, you must make sure
to SAVE the previous value, and restore it when your script ends... or it
will remain that way until Intellicomm is exited/reloaded, or until a new
main setup file is loaded (LOADINI, or a manual "Load" via the Icom main
setup menu). As with all variables, you are modifying memory locations
and you do not change data on-disk when you modify a variable. You must
use SAVEINI to update the Icom Main Setup file on-disk. See the Main
Setup Variables section in SCRIPT.DOC for more details.
There are many other catalog-related commands, including: CCLEARBUF
(clears all the $XXX_FLD variables, and the file description memory
buffer, perhaps to get ready to fill in a new record yourself before
CADDREC), CDELREC, CEDITREC (same as "Edit" from the File Tagger; [PgUp]
and [PgDn] can also be used to browse through the database), CEMPTY,
CEXPORT, CFLUSH, CIMPORTNEW, CIMPORTTEXT, CNOTEREC, CPACK, CTAGREC and
several others. They all are quite simple to use, and most just perform
tasks you normally carry out right from the Tagger, so none should
require much explaining if you understand the Tagger, and the material
above. You can find all the necessary information, along with examples,
in the Detailed Command Summaries in SCRIPT.DOC. To see a quick summary
of all the Catalog-oriented commands, see the "Script Commands at a
Glance" secion in SCRIPT.DOC.
Intellicomm v2.01 SCRTUTOR.DOC 74
7. USING THE SCRIPT DEBUGGER
7.1 What are BUGS, and What is a DEBUGGER?
What! Bugs in MY script?! No matter how hard you try, no matter how
careful you are about writing your scripts, somewhere, sometime you're
probably going to run into a problem that you simply can't figure out.
You'll look the script over and pull your hair out: everything will LOOK
fine. You'll check the manual; you'll check your commands again, you'll
check the manual again ... and just before you're ready to throw your
computer out the window you'll remember the Debugger!
Debuggers aren't designed to find your programming errors for you:
they're designed to slow your program down (or script, in this case), run
it step-by-step, show you each command before it gets executed, and show
you the results of each command (screen output, etc) after it is
executed. Sometimes it's the only way to pick up subtle errors... and
subtle errors can sometimes create large problems.
Bugs are not syntax errors such as misspelling a command/variable or the
like: Icom will pick those up and abort the script, pointing the error
out to you. Bugs are logic errors, where you're perhaps meaning to see
if something is less than or equal to a number... and you're instead
(unintentionally) telling Icom to check whether the number is GREATER
than or equal to the number. Or you have two variables called X and Y,
and you use Y when you meant to use X, and so forth. These sorts of
errors cannot be picked up by Icom's script processor (or any programming
language for that matter), since it trusts that you know what you're
doing... It doesn't second-guess you, and has no way of knowing whether
you REALLY wanted to use X when you used Y, or wanted greater than (>)
and used less than (<) by mistake. This is where the debugger comes in.
Here are some examples of the kinds of bugs you're likely to run into,
that you can find with the debugger:
1. Garbage In Garbage Out. This means that if the data you're using is
garbage (not valid) then the results you'll get will also be garbage.
It applies to anything that uses any sort of data (script command
parameters and all script variables are 'data') to perform a function.
The debugger can help eliminate this problem by showing you the
CONTENTS of variables (the data), before the script command acts upon
the data. A common mistake is to inadvertently change the contents of
a variable somewhere else (in the same script; a subroutine perhaps)
without realizing it. You'll *think* that a certain variable is
holding a certain value... but you'll have forgotten that the same
variable is used elsewhere in the script, and is thus modified
elsewhere. By using the debugger you can follow the script step-by-
step to find where your data is corrupted.
2. Another common error is to forget to initialize one or more variables
before entering a loop. If you were using a variable called 'count',
and you had previously been using 'count' for something and its value
was now higher than 10, then this loop below would NOT execute at all:
Intellicomm v2.01 SCRTUTOR.DOC 75
while count <= 10 ;while 'count' is less than or equal to 10
...
inc count ;increment count (count = count + 1)
endwhile
By using the debugger you'd see this on the bottom screen line before
the WHILE command was executed, if 'count' had a value of 15:
5: WHILE 15 <= 10
The debugger pauses prior to executing each line and shows you the
script line number it's about to execute (5 in the example above)
followed by a colon and the command. But instead of showing you the
variable names it first REPLACES the variable names with the data that
is stored in the variable. Your mistake would then be obvious (15
isn't less than or equal to 10), and you could abort the script and
modify it so that 'count' was properly initialized to whatever it
should be initialized to before entering the loop:
assign count 1 ;this was missing... now the loop works fine
while count <= 10
...
inc count ;increment count (count = count + 1)
endwhile
3. Variable mix-ups. You think variable 'Y' is holding a certain value,
but you're mistaken and actually variable 'X' has the value you
wanted. Using the debugger, with the values of variables displayed on
the status line, you'll be able to pick up the mistake.
4. Logic errors. You're comparing two values, and either get the
arguments mixed up left to right, or use the wrong operator in between
(less than instead of greater than, etc). If you're expecting an IF
or WHILE comparison to be TRUE, and it turns out to be false and skips
commands you didn't expect it to, then you'll know that the problem is
in your IF or WHILE comparison and can take a closer look. Often you
can only pick this up by executing the script line-by-line in debug
mode and watching closely. With the script running at full tilt you
might not even realize that the comparison had proven false, and that
one or more script lines were skipped as a result.
There are all sorts of tiny errors like this that can cause scripts (and
computer programs in general) to malfunction. If you ever wondered where
program bugs come from... and thought it was due to shabby work, now you
know different. <grin> The types of errors that cause script or program
bugs are often so subtle that the only way to find them is to execute the
program one line at a time... and even then you'll often have to watch
carefully.
7.2 Using the Debugger
To activate the debugger while a script is EXECUTING simply press [Alt-Q]
(the usual job/script 'Q'uit command), then select "Script DEBUG [Step-
by-step]" or "Script DEBUG [Animate]" from the Alt-Q menu. To turn
Intellicomm v2.01 SCRTUTOR.DOC 76
debugging off, press [Alt-Q] and select "Script DEBUG [OFF]".
In 'step-by-step' mode, the debugger displays each script line as
outlined above, then waits for you to press the [Space] bar, [Enter] key,
or left mouse button before it executes the line. In 'animate' mode,
each script line is displayed momentarily before it executes, running at
about 2 lines per second (to quickly zero in on the general area of a
problem, when you're not quite sure where to start looking).
You can also turn step-by-step and animate mode on/off around lines of
your script where you suspect a problem:
DEBUG 1 ;turn step-by-step mode ON
DEBUG 2 ;turn animate mode ON
DEBUG 0 ;turn step-by-step or animate mode OFF
This is how you'll normally turn debug mode on and off, by adding DEBUG
commands to your script in and around lines where you suspect a problem.
Pressing the [Alt-Q] key and selecting a debug mode manually might be
difficult to time properly.
When either debug mode is on, Icom will pause prior to executing each
line and will show you the line it's about to execute (with all the
relevant variables replaced to show the VALUE of the variable). To see
the script line in its original format (with the variable names instead
of the contents of the variables), press and hold the [Alt] key. When
you release the [Alt] key the line will revert back to the original
format, showing the value of the variables. This can be helpful for
cases where you get two variables mixed up, and you intended to use X,
but you used Y by accident. When you see the 'wrong' value displayed,
hold down the [Alt] key to see the NAME of the variable you used. You
might have used the wrong variable by mistake. The [Alt] key can also be
useful for pausing animate mode temporarily. Animate mode pauses for as
long as you hold the [Alt] key down (and you can also press [Alt-Q]
before releasing [Alt] to switch from animate mode to step-by-step mode).
If the line is longer than can be displayed on the screen, use the
[Left], [Right] cursor arrow keys (or mouse left/right motion) to scroll
the line left/right.
If you find a bug and wish to abort the script, again it's done via the
[Alt-Q] menu by selecting "Abort Script Only". To get a quick summary of
all these keys, press the [F1] help key when in either debugging mode.
NOTE 1: Script commands which permit OTHER commands to be executed if a
certain condition is true (DIREXIST, EXIST, OFFLINE, ONLINE, etc) will
show up on the debug display TWICE if the condition is true. For
example, with an OFFLINE command you'd see something like this on the
debug display first:
5: OFFLINE GOTO exit_script
...then, if the modem WAS offline (the condition was true), you'd see
this on the debug display, before the specified command was executed:
Intellicomm v2.01 SCRTUTOR.DOC 77
5: GOTO exit_script
The same line number is displayed since the second command is on the same
line.
NOTE 2: If a command takes a parameter that will be changed as soon as
you press the [Space] bar ('ADD z y x' for example would assign y + x to
z immediately), then the debugger doesn't bother showing you the contents
of the variable, and simply shows you the variable name. It's more
useful (and easier to follow) to be able to see which variable you're
assigning to, rather than looking at something like this:
7: ADD 0 123 456 ;where 5 would be the CONTENTS of a variable
(Where '0' just happened to be the contents of 'z' BEFORE the ADD command
was executed.) In these cases, the debugger displays:
7: ADD myvariable 123 456
This applies only to certain commands where it may prove confusing to
display the contents of the variable rather than the variable name, as
with ADD above, where you might get the impression that the ADD had
already taken effect and the WRONG value had been stored (which would not
be the case, since the command has not executed as yet). There are some
cases, such as with ADDSLASH where a variable IS going to be changed, but
the debugger does display the contents of the variable:
8: ADDSLASH "C:\SOMEDIR"
is more useful than:
8: ADDSLASH mydirectory
would be. If you want to check the contents of such variables AFTER the
command executes, and you don't have any lines below that use the
variable, use the DEBUG command as outlined below.
7.3 Checking the Contents of a Single Variable
To have the debugger simply display the contents of a single variable to
the screen and wait for you to press [Space] or [Enter], specify the
variable name after the DEBUG command:
variable myvariable ;define a variable called 'myvariable'
...
GETS myvariable 10 ;get keyboard input to 'myvariable'
DEBUG myvariable ;pause and display the contents of 'myvariable'
...
capture "NEWCAP.CAP" ;open a new capture file
DEBUG $CAP_FNAME ;display the full '$CAP_NAME' (drive/path, etc).
The two DEBUG commands above would might display something like this:
10: DEBUG "User Input"
Intellicomm v2.01 SCRTUTOR.DOC 78
20: DEBUG "C:\ICOM\CAP\NEWCAP.CAP"
This allows you to see the contents of a variable without actually
'doing' anything. You don't have to be in DEBUG mode (i.e. by pressing
[Alt-Q]) to use this command. You can place them anywhere in your
script, wherever you suspect a problem, and Icom will switch to debug
mode temporarily to display the contents of the specified variable.
If you ARE in debug mode and a 'DEBUG variable' command is found, the
command is simply displayed as any other command would be. If in animate
mode, this use of DEBUG also causes the debugger to pause and wait for
[Space] or [Enter] before continuing in animate mode.
Again, press and hold the [Alt] key to see the original script line (the
variable name instead of the contents of the variable). After you've
checked the contents of the variable, press [Space] or [Enter] (or the
left mouse button) to continue running the script at full speed (or to
continue in Animate mode if you were in Animate mode previously), or
again use the [Alt-Q] menu to either abort the script, or go into step-
by-step or animate mode, etc.
7.4 Debug Hotkeys
When debug mode is active, most of the Icom hotkeys are disabled. You
cannot, for example, press [Alt-M] to switch to the Main Menu. You can,
however, use [Alt-A] to enter the editor, [Alt-J] to 'Jump' to a DOS
shell, [Alt-N] (user-defined Hotkey 1), [Alt-O] (user-defined hotkey 2),
and [Alt-V] to run your external archiver. When you finish with the
called task, you'll be returned to the debug display exactly as it was
before you pressed the hotkey.
IMPORTANT: If you use [Alt-A] to run the editor and change the script
Icom is running, the changes will not take effect until you abort and re-
start the script. Icom loads scripts into memory, and changing the
script file (on-disk) in the editor doesn't affect the script in memory
that Icom is running.